From 18db58a87bae02b45b808a0e9369b5dce57ee470 Mon Sep 17 00:00:00 2001 From: M1 AIR Date: Sat, 7 Feb 2026 00:14:58 +0700 Subject: [PATCH 01/12] test: MR pipeline notification --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5b502da1..4839caf4 100644 --- a/README.md +++ b/README.md @@ -111,3 +111,4 @@ IT Development PT Mitra Berlian Unggas Group ## 📃 License > This project is private. All rights reserved. +# mr test Sat 7 Feb 2026 00:14:58 WIB From e9d1cca294fea5afb8c3be84255c78e80895189b Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 3 Mar 2026 05:12:05 +0000 Subject: [PATCH 02/12] Edit development.yml --- ci/development.yml | 214 +++++++++++++++++++++++++++++---------------- 1 file changed, 141 insertions(+), 73 deletions(-) diff --git a/ci/development.yml b/ci/development.yml index 7b4733b5..7c01f58b 100644 --- a/ci/development.yml +++ b/ci/development.yml @@ -1,91 +1,159 @@ stages: - - deploy + - build + - gitops -deploy-dev: - stage: deploy - image: alpine:3.20 +variables: + AWS_REGION: ap-southeast-3 + ECR_REGISTRY: 886436954922.dkr.ecr.ap-southeast-3.amazonaws.com + ECR_REPO_NAME: mbugroup/lti-api + ECR_REPOSITORY: ${ECR_REGISTRY}/${ECR_REPO_NAME} + DOCKER_HOST: unix:///var/run/docker.sock + DOCKER_TLS_CERTDIR: "" + DOCKER_BUILDKIT: "1" + +workflow: + rules: + - if: '$CI_COMMIT_BRANCH' + +# ========================= +# Helper: login ECR +# ========================= +.ecr_login: &ecr_login | + AWS_CLI_ENV_ARGS="" + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_REGION=$AWS_REGION" + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}" + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}" + if [ -n "${AWS_SESSION_TOKEN:-}" ]; then + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN" + fi + + PASS="$(docker run --rm $AWS_CLI_ENV_ARGS public.ecr.aws/aws-cli/aws-cli:latest \ + ecr get-login-password --region "$AWS_REGION" || true)" + if [ -z "$PASS" ]; then + echo "ERROR: Failed to get ECR login password." + exit 1 + fi + echo "$PASS" | docker login --username AWS --password-stdin "$ECR_REGISTRY" + +# ========================= +# DEV +# ========================= +build_push_dev_lti: + stage: build + image: public.ecr.aws/docker/library/docker:27 + tags: [self-hosted-dev] rules: - if: '$CI_COMMIT_BRANCH == "development"' - when: on_success - - when: never - variables: - DEPLOY_APP: "LTI-MBUGROUP" - GIT_SUBMODULE_STRATEGY: recursive - GIT_DEPTH: "1" - + IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" before_script: - - echo "🧰 Installing dependencies..." - - apk update && apk add --no-cache openssh git curl bash + - set -eu + - docker version + - docker info + - *ecr_login + script: | + set -eu + echo "Build & push: $ECR_REPOSITORY:$IMAGE_TAG" - # Setup SSH di runner - - mkdir -p ~/.ssh - - echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa - - chmod 600 ~/.ssh/id_rsa - - eval "$(ssh-agent -s)" - - ssh-add ~/.ssh/id_rsa + docker build \ + -t "$ECR_REPOSITORY:$IMAGE_TAG" \ + . - # Trust host keys (server + gitlab) biar SSH gak nanya interaktif - - ssh-keyscan -H "$SERVER_IP" >> ~/.ssh/known_hosts - - ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts + docker push "$ECR_REPOSITORY:$IMAGE_TAG" - script: - - echo "🚀 Deploying latest code to $SERVER_USER@$SERVER_IP" - - > - if ssh -o StrictHostKeyChecking=no "$SERVER_USER@$SERVER_IP" " - set -e +update_gitops_dev_lti: + stage: gitops + image: alpine:3.20 + tags: [self-hosted-dev] + rules: + - if: '$CI_COMMIT_BRANCH == "development"' + needs: ["build_push_dev_lti"] + variables: + IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" + GITOPS_BRANCH: main + VALUES_FILE: environments/lti/dev/lti-values-dev.yaml + GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git + before_script: + - set -eu + - apk add --no-cache git yq + - git config --global user.email "ci@gitlab" + - git config --global user.name "gitlab-ci" + script: | + set -eu + rm -rf gitops + git clone --depth 1 --branch "$GITOPS_BRANCH" "$GITOPS_REPO_URL" gitops + cd gitops - cd /home/devops/docker/deployment/development/lti-api + echo "Updating DEV image.tag to $IMAGE_TAG in $VALUES_FILE" + yq -i '.image.repository = strenv(ECR_REPOSITORY)' "$VALUES_FILE" + yq -i '.image.tag = strenv(IMAGE_TAG)' "$VALUES_FILE" - # Pastikan remote origin SSH (antisipasi kalau pernah ke-set HTTPS) - git remote set-url origin git@gitlab.com:mbugroup/lti-api.git + git add "$VALUES_FILE" + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "lti dev deploy ${IMAGE_TAG}" + git push origin "$GITOPS_BRANCH" - # Pastikan server percaya gitlab.com juga (untuk git fetch via SSH) - mkdir -p ~/.ssh - ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts +# ========================= +# PROD +# ========================= +# build_push_prod_lti: +# stage: build +# image: public.ecr.aws/docker/library/docker:27 +# tags: [self-hosted-dev] +# rules: +# - if: '$CI_COMMIT_BRANCH == "production"' +# variables: +# IMAGE_TAG: "prod-${CI_COMMIT_SHORT_SHA}" +# before_script: +# - set -eu +# - docker version +# - docker info +# - *ecr_login +# script: | +# set -eu +# echo "Build & push: $ECR_REPOSITORY:$IMAGE_TAG" - # Fetch/reset pakai SSH - GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no' git fetch origin development - git reset --hard origin/development +# docker build \ +# -t "$ECR_REPOSITORY:$IMAGE_TAG" \ +# . - docker compose restart dev-api-lti || docker compose up -d dev-api-lti - "; then - STATUS='success'; - else - STATUS='failed'; - fi; +# docker push "$ECR_REPOSITORY:$IMAGE_TAG" - RUN_URL="${CI_PROJECT_URL}/-/pipelines/${CI_PIPELINE_ID}"; +# update_gitops_prod_lti: +# stage: gitops +# image: alpine:3.20 +# tags: [self-hosted-dev] +# rules: +# - if: '$CI_COMMIT_BRANCH == "production"' +# needs: ["build_push_prod_lti"] +# variables: +# IMAGE_TAG: "prod-${CI_COMMIT_SHORT_SHA}" +# GITOPS_BRANCH: main +# VALUES_FILE: environments/lti/prod/lti-values-prod.yaml +# GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git +# before_script: +# - set -eu +# - apk add --no-cache git yq +# - git config --global user.email "ci@gitlab" +# - git config --global user.name "gitlab-ci" +# script: | +# set -eu +# rm -rf gitops +# git clone --depth 1 --branch "$GITOPS_BRANCH" "$GITOPS_REPO_URL" gitops +# cd gitops - if [ "$STATUS" = "success" ]; then - COLOR=3066993; - TITLE="✅ Deployment API Succeeded"; - DESC="Deployment job on branch \`${CI_COMMIT_REF_NAME}\` completed successfully."; - else - COLOR=15158332; - TITLE="❌ Deployment API Failed Gaes"; - DESC="Deployment job on branch \`${CI_COMMIT_REF_NAME}\` failed."; - fi; +# echo "Updating PROD image.tag to $IMAGE_TAG in $VALUES_FILE" +# yq -i '.image.repository = strenv(ECR_REPOSITORY)' "$VALUES_FILE" +# yq -i '.image.tag = strenv(IMAGE_TAG)' "$VALUES_FILE" - echo "{ - \"username\": \"CI Bot\", - \"embeds\": [{ - \"title\": \"$TITLE\", - \"description\": \"$DESC\", - \"color\": $COLOR, - \"fields\": [ - {\"name\": \"Repository\", \"value\": \"${CI_PROJECT_PATH}\", \"inline\": true}, - {\"name\": \"Actor\", \"value\": \"${GITLAB_USER_LOGIN}\", \"inline\": true}, - {\"name\": \"Commit\", \"value\": \"${CI_COMMIT_SHA}\", \"inline\": false}, - {\"name\": \"Pipeline\", \"value\": \"[Open run](${RUN_URL})\", \"inline\": false} - ] - }] - }" > payload.json; - - echo "📡 Sending notification to Discord..."; - curl -sS -H "Content-Type: application/json" \ - -d @payload.json "$DISCORD_WEBHOOK_URL"; - - environment: - name: development +# git add "$VALUES_FILE" +# if git diff --cached --quiet; then +# echo "No changes to commit" +# exit 0 +# fi +# git commit -m "lti prod deploy ${IMAGE_TAG}" +# git push origin "$GITOPS_BRANCH" \ No newline at end of file From 0206d9dc68de1d6f216464ffafbfaf8b509a3e4e Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 3 Mar 2026 05:34:21 +0000 Subject: [PATCH 03/12] Edit development.yml --- ci/development.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/development.yml b/ci/development.yml index 7c01f58b..9b3a757f 100644 --- a/ci/development.yml +++ b/ci/development.yml @@ -64,7 +64,7 @@ build_push_dev_lti: update_gitops_dev_lti: stage: gitops - image: alpine:3.20 + image: public.ecr.aws/docker/library/alpine:3.20 tags: [self-hosted-dev] rules: - if: '$CI_COMMIT_BRANCH == "development"' @@ -125,7 +125,7 @@ update_gitops_dev_lti: # update_gitops_prod_lti: # stage: gitops -# image: alpine:3.20 +# image: public.ecr.aws/docker/library/alpine:3.20 # tags: [self-hosted-dev] # rules: # - if: '$CI_COMMIT_BRANCH == "production"' From aafbba199d67c0de428af0271c0a1146ed2124f7 Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 3 Mar 2026 07:06:05 +0000 Subject: [PATCH 04/12] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 256 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 229 insertions(+), 27 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2417298f..5d9ce462 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,35 +1,237 @@ +stages: + - build + - gitops + +variables: + AWS_REGION: ap-southeast-3 + ECR_REGISTRY: 886436954922.dkr.ecr.ap-southeast-3.amazonaws.com + ECR_REPO_NAME: mbugroup/mas-presensi + ECR_REPOSITORY: ${ECR_REGISTRY}/${ECR_REPO_NAME} + + DOCKER_HOST: unix:///var/run/docker.sock + DOCKER_TLS_CERTDIR: "" + DOCKER_BUILDKIT: "1" + workflow: rules: - # MR pipeline + # run untuk push & MR + - if: '$CI_PIPELINE_SOURCE == "push"' - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - when: always - - # Push pipeline hanya untuk env branch - - if: '$CI_COMMIT_BRANCH == "development"' - when: always - - if: '$CI_COMMIT_BRANCH == "staging"' - when: always - - if: '$CI_COMMIT_BRANCH == "production"' - when: always - - # Selain itu jangan buat pipeline - when: never -include: - # khusus MR (notif) - - local: "ci/merge_request.yml" - rules: - - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' +# ========================= +# Helper: login ECR +# ========================= +.ecr_login: &ecr_login | + AWS_CLI_ENV_ARGS="" + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_REGION=$AWS_REGION" + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}" + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}" + if [ -n "${AWS_SESSION_TOKEN:-}" ]; then + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN" + fi - # khusus push ke branch env - - local: "ci/development.yml" - rules: - - if: '$CI_COMMIT_BRANCH == "development"' + PASS="$(docker run --rm $AWS_CLI_ENV_ARGS public.ecr.aws/aws-cli/aws-cli:latest \ + ecr get-login-password --region "$AWS_REGION" || true)" + if [ -z "$PASS" ]; then + echo "ERROR: Failed to get ECR login password." + exit 1 + fi + echo "$PASS" | docker login --username AWS --password-stdin "$ECR_REGISTRY" - - local: "ci/staging.yml" - rules: - - if: '$CI_COMMIT_BRANCH == "staging"' +# ========================= +# DEV (push ke development) +# ========================= +build_push_dev: + stage: build + image: public.ecr.aws/docker/library/docker:27 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' + variables: + IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" + before_script: + - set -eu + - docker version + - docker info + - *ecr_login + script: | + set -eu + echo "Build & push: $ECR_REPOSITORY:$IMAGE_TAG" + BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" - - local: "ci/production.yml" - rules: - - if: '$CI_COMMIT_BRANCH == "production"' + n=1 + until [ "$n" -gt 3 ]; do + docker pull "$BASE_IMAGE" && break + echo "Pull base image failed (attempt $n/3), retrying..." + sleep $((n * 10)) + n=$((n + 1)) + done + [ "$n" -le 3 ] || (echo "ERROR: Failed pulling base image: $BASE_IMAGE" && exit 1) + + docker build \ + --build-arg NODE_IMAGE="$BASE_IMAGE" \ + --build-arg NODE_ENV="${NODE_ENV:-dev}" \ + --build-arg PORT="${PORT:-3000}" \ + --build-arg NEXT_PUBLIC_API_BASE_URL="${NEXT_PUBLIC_API_BASE_URL:-/api}" \ + --build-arg DATABASE_URL="${DATABASE_URL:-}" \ + --build-arg AUTH_JWT_SECRET="${AUTH_JWT_SECRET:-}" \ + --build-arg NEXT_PUBLIC_POWERSYNC_URL="${NEXT_PUBLIC_POWERSYNC_URL:-}" \ + --build-arg POWERSYNC_PRIVATE_KEY="${POWERSYNC_PRIVATE_KEY:-}" \ + --build-arg POWERSYNC_PUBLIC_KEY="${POWERSYNC_PUBLIC_KEY:-}" \ + -t "$ECR_REPOSITORY:$IMAGE_TAG" \ + . + + docker push "$ECR_REPOSITORY:$IMAGE_TAG" + +update_gitops_dev_presensi: + stage: gitops + image: public.ecr.aws/docker/library/alpine:3.20 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' + needs: ["build_push_dev"] + variables: + IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" + GITOPS_BRANCH: main + VALUES_FILE: environments/mas-presensi/dev/mas-presensi-values-dev.yaml + GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git + before_script: + - set -eu + - apk add --no-cache git yq + - git config --global user.email "ci@gitlab" + - git config --global user.name "gitlab-ci" + script: | + set -eu + rm -rf gitops + git clone --depth 1 --branch "$GITOPS_BRANCH" "$GITOPS_REPO_URL" gitops + cd gitops + + echo "Updating dev image.tag to $IMAGE_TAG" + yq -i '.image.tag = strenv(IMAGE_TAG)' "$VALUES_FILE" + + git add "$VALUES_FILE" + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "mas-presensi dev deploy ${IMAGE_TAG}" + git push origin "$GITOPS_BRANCH" + +# ========================= +# PROD +# 1) MR dev -> prod (merge_request_event, target production) : build optional push +# 2) Setelah merge (push ke branch production) : build + push + update gitops +# ========================= + +# (A) MR pipeline (validate build dari state MR) +build_prod_mr: + stage: build + image: public.ecr.aws/docker/library/docker:27 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "production"' + variables: + IMAGE_TAG: "prod-mr-${CI_COMMIT_SHORT_SHA}" + # kalau mau push juga saat MR, set di project/CI variable: PUSH_IMAGE=true + PUSH_IMAGE: "${PUSH_IMAGE:-false}" + before_script: + - set -eu + - docker version + - docker info + - *ecr_login + script: | + set -eu + echo "Build (MR) : $ECR_REPOSITORY:$IMAGE_TAG" + BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" + docker pull "$BASE_IMAGE" || true + + docker build \ + --build-arg NODE_IMAGE="$BASE_IMAGE" \ + --build-arg NODE_ENV="${NODE_ENV:-production}" \ + -t "$ECR_REPOSITORY:$IMAGE_TAG" \ + . + + if [ "$PUSH_IMAGE" = "true" ]; then + echo "Pushing image for MR..." + docker push "$ECR_REPOSITORY:$IMAGE_TAG" + else + echo "Skip push (MR)." + fi + +# (B) push ke production (hasil merge) => ini yang “build dari code merge” +build_push_prod: + stage: build + image: public.ecr.aws/docker/library/docker:27 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' + variables: + IMAGE_TAG: "prod-${CI_COMMIT_SHORT_SHA}" + before_script: + - set -eu + - docker version + - docker info + - *ecr_login + script: | + set -eu + echo "Build & push (prod): $ECR_REPOSITORY:$IMAGE_TAG" + BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" + + n=1 + until [ "$n" -gt 3 ]; do + docker pull "$BASE_IMAGE" && break + echo "Pull base image failed (attempt $n/3), retrying..." + sleep $((n * 10)) + n=$((n + 1)) + done + [ "$n" -le 3 ] || (echo "ERROR: Failed pulling base image: $BASE_IMAGE" && exit 1) + + docker build \ + --build-arg NODE_IMAGE="$BASE_IMAGE" \ + --build-arg NODE_ENV="${NODE_ENV:-production}" \ + --build-arg PORT="${PORT:-3000}" \ + --build-arg NEXT_PUBLIC_API_BASE_URL="${NEXT_PUBLIC_API_BASE_URL:-/api}" \ + --build-arg DATABASE_URL="${DATABASE_URL:-}" \ + --build-arg AUTH_JWT_SECRET="${AUTH_JWT_SECRET:-}" \ + --build-arg NEXT_PUBLIC_POWERSYNC_URL="${NEXT_PUBLIC_POWERSYNC_URL:-}" \ + --build-arg POWERSYNC_PRIVATE_KEY="${POWERSYNC_PRIVATE_KEY:-}" \ + --build-arg POWERSYNC_PUBLIC_KEY="${POWERSYNC_PUBLIC_KEY:-}" \ + -t "$ECR_REPOSITORY:$IMAGE_TAG" \ + . + + docker push "$ECR_REPOSITORY:$IMAGE_TAG" + +update_gitops_prod_presensi: + stage: gitops + image: public.ecr.aws/docker/library/alpine:3.20 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' + needs: ["build_push_prod"] + variables: + IMAGE_TAG: "prod-${CI_COMMIT_SHORT_SHA}" + GITOPS_BRANCH: main + VALUES_FILE: environments/mas-presensi/prod/mas-presensi-values-prod.yaml + GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git + before_script: + - set -eu + - apk add --no-cache git yq + - git config --global user.email "ci@gitlab" + - git config --global user.name "gitlab-ci" + script: | + set -eu + rm -rf gitops + git clone --depth 1 --branch "$GITOPS_BRANCH" "$GITOPS_REPO_URL" gitops + cd gitops + + echo "Updating prod image.tag to $IMAGE_TAG" + yq -i '.image.tag = strenv(IMAGE_TAG)' "$VALUES_FILE" + + git add "$VALUES_FILE" + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "mas-presensi prod deploy ${IMAGE_TAG}" + git push origin "$GITOPS_BRANCH" \ No newline at end of file From 55451a5ea886558fee32691bd809292225ff3664 Mon Sep 17 00:00:00 2001 From: kris Date: Tue, 3 Mar 2026 07:16:29 +0000 Subject: [PATCH 05/12] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 256 ++++++------------------------------------------- 1 file changed, 27 insertions(+), 229 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5d9ce462..2417298f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,237 +1,35 @@ -stages: - - build - - gitops - -variables: - AWS_REGION: ap-southeast-3 - ECR_REGISTRY: 886436954922.dkr.ecr.ap-southeast-3.amazonaws.com - ECR_REPO_NAME: mbugroup/mas-presensi - ECR_REPOSITORY: ${ECR_REGISTRY}/${ECR_REPO_NAME} - - DOCKER_HOST: unix:///var/run/docker.sock - DOCKER_TLS_CERTDIR: "" - DOCKER_BUILDKIT: "1" - workflow: rules: - # run untuk push & MR - - if: '$CI_PIPELINE_SOURCE == "push"' + # MR pipeline - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + when: always + + # Push pipeline hanya untuk env branch + - if: '$CI_COMMIT_BRANCH == "development"' + when: always + - if: '$CI_COMMIT_BRANCH == "staging"' + when: always + - if: '$CI_COMMIT_BRANCH == "production"' + when: always + + # Selain itu jangan buat pipeline - when: never -# ========================= -# Helper: login ECR -# ========================= -.ecr_login: &ecr_login | - AWS_CLI_ENV_ARGS="" - AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_REGION=$AWS_REGION" - AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}" - AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}" - if [ -n "${AWS_SESSION_TOKEN:-}" ]; then - AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN" - fi +include: + # khusus MR (notif) + - local: "ci/merge_request.yml" + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - PASS="$(docker run --rm $AWS_CLI_ENV_ARGS public.ecr.aws/aws-cli/aws-cli:latest \ - ecr get-login-password --region "$AWS_REGION" || true)" - if [ -z "$PASS" ]; then - echo "ERROR: Failed to get ECR login password." - exit 1 - fi - echo "$PASS" | docker login --username AWS --password-stdin "$ECR_REGISTRY" + # khusus push ke branch env + - local: "ci/development.yml" + rules: + - if: '$CI_COMMIT_BRANCH == "development"' -# ========================= -# DEV (push ke development) -# ========================= -build_push_dev: - stage: build - image: public.ecr.aws/docker/library/docker:27 - tags: [self-hosted-dev] - rules: - - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' - variables: - IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" - before_script: - - set -eu - - docker version - - docker info - - *ecr_login - script: | - set -eu - echo "Build & push: $ECR_REPOSITORY:$IMAGE_TAG" - BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" + - local: "ci/staging.yml" + rules: + - if: '$CI_COMMIT_BRANCH == "staging"' - n=1 - until [ "$n" -gt 3 ]; do - docker pull "$BASE_IMAGE" && break - echo "Pull base image failed (attempt $n/3), retrying..." - sleep $((n * 10)) - n=$((n + 1)) - done - [ "$n" -le 3 ] || (echo "ERROR: Failed pulling base image: $BASE_IMAGE" && exit 1) - - docker build \ - --build-arg NODE_IMAGE="$BASE_IMAGE" \ - --build-arg NODE_ENV="${NODE_ENV:-dev}" \ - --build-arg PORT="${PORT:-3000}" \ - --build-arg NEXT_PUBLIC_API_BASE_URL="${NEXT_PUBLIC_API_BASE_URL:-/api}" \ - --build-arg DATABASE_URL="${DATABASE_URL:-}" \ - --build-arg AUTH_JWT_SECRET="${AUTH_JWT_SECRET:-}" \ - --build-arg NEXT_PUBLIC_POWERSYNC_URL="${NEXT_PUBLIC_POWERSYNC_URL:-}" \ - --build-arg POWERSYNC_PRIVATE_KEY="${POWERSYNC_PRIVATE_KEY:-}" \ - --build-arg POWERSYNC_PUBLIC_KEY="${POWERSYNC_PUBLIC_KEY:-}" \ - -t "$ECR_REPOSITORY:$IMAGE_TAG" \ - . - - docker push "$ECR_REPOSITORY:$IMAGE_TAG" - -update_gitops_dev_presensi: - stage: gitops - image: public.ecr.aws/docker/library/alpine:3.20 - tags: [self-hosted-dev] - rules: - - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' - needs: ["build_push_dev"] - variables: - IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" - GITOPS_BRANCH: main - VALUES_FILE: environments/mas-presensi/dev/mas-presensi-values-dev.yaml - GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git - before_script: - - set -eu - - apk add --no-cache git yq - - git config --global user.email "ci@gitlab" - - git config --global user.name "gitlab-ci" - script: | - set -eu - rm -rf gitops - git clone --depth 1 --branch "$GITOPS_BRANCH" "$GITOPS_REPO_URL" gitops - cd gitops - - echo "Updating dev image.tag to $IMAGE_TAG" - yq -i '.image.tag = strenv(IMAGE_TAG)' "$VALUES_FILE" - - git add "$VALUES_FILE" - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "mas-presensi dev deploy ${IMAGE_TAG}" - git push origin "$GITOPS_BRANCH" - -# ========================= -# PROD -# 1) MR dev -> prod (merge_request_event, target production) : build optional push -# 2) Setelah merge (push ke branch production) : build + push + update gitops -# ========================= - -# (A) MR pipeline (validate build dari state MR) -build_prod_mr: - stage: build - image: public.ecr.aws/docker/library/docker:27 - tags: [self-hosted-dev] - rules: - - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "production"' - variables: - IMAGE_TAG: "prod-mr-${CI_COMMIT_SHORT_SHA}" - # kalau mau push juga saat MR, set di project/CI variable: PUSH_IMAGE=true - PUSH_IMAGE: "${PUSH_IMAGE:-false}" - before_script: - - set -eu - - docker version - - docker info - - *ecr_login - script: | - set -eu - echo "Build (MR) : $ECR_REPOSITORY:$IMAGE_TAG" - BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" - docker pull "$BASE_IMAGE" || true - - docker build \ - --build-arg NODE_IMAGE="$BASE_IMAGE" \ - --build-arg NODE_ENV="${NODE_ENV:-production}" \ - -t "$ECR_REPOSITORY:$IMAGE_TAG" \ - . - - if [ "$PUSH_IMAGE" = "true" ]; then - echo "Pushing image for MR..." - docker push "$ECR_REPOSITORY:$IMAGE_TAG" - else - echo "Skip push (MR)." - fi - -# (B) push ke production (hasil merge) => ini yang “build dari code merge” -build_push_prod: - stage: build - image: public.ecr.aws/docker/library/docker:27 - tags: [self-hosted-dev] - rules: - - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' - variables: - IMAGE_TAG: "prod-${CI_COMMIT_SHORT_SHA}" - before_script: - - set -eu - - docker version - - docker info - - *ecr_login - script: | - set -eu - echo "Build & push (prod): $ECR_REPOSITORY:$IMAGE_TAG" - BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" - - n=1 - until [ "$n" -gt 3 ]; do - docker pull "$BASE_IMAGE" && break - echo "Pull base image failed (attempt $n/3), retrying..." - sleep $((n * 10)) - n=$((n + 1)) - done - [ "$n" -le 3 ] || (echo "ERROR: Failed pulling base image: $BASE_IMAGE" && exit 1) - - docker build \ - --build-arg NODE_IMAGE="$BASE_IMAGE" \ - --build-arg NODE_ENV="${NODE_ENV:-production}" \ - --build-arg PORT="${PORT:-3000}" \ - --build-arg NEXT_PUBLIC_API_BASE_URL="${NEXT_PUBLIC_API_BASE_URL:-/api}" \ - --build-arg DATABASE_URL="${DATABASE_URL:-}" \ - --build-arg AUTH_JWT_SECRET="${AUTH_JWT_SECRET:-}" \ - --build-arg NEXT_PUBLIC_POWERSYNC_URL="${NEXT_PUBLIC_POWERSYNC_URL:-}" \ - --build-arg POWERSYNC_PRIVATE_KEY="${POWERSYNC_PRIVATE_KEY:-}" \ - --build-arg POWERSYNC_PUBLIC_KEY="${POWERSYNC_PUBLIC_KEY:-}" \ - -t "$ECR_REPOSITORY:$IMAGE_TAG" \ - . - - docker push "$ECR_REPOSITORY:$IMAGE_TAG" - -update_gitops_prod_presensi: - stage: gitops - image: public.ecr.aws/docker/library/alpine:3.20 - tags: [self-hosted-dev] - rules: - - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' - needs: ["build_push_prod"] - variables: - IMAGE_TAG: "prod-${CI_COMMIT_SHORT_SHA}" - GITOPS_BRANCH: main - VALUES_FILE: environments/mas-presensi/prod/mas-presensi-values-prod.yaml - GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git - before_script: - - set -eu - - apk add --no-cache git yq - - git config --global user.email "ci@gitlab" - - git config --global user.name "gitlab-ci" - script: | - set -eu - rm -rf gitops - git clone --depth 1 --branch "$GITOPS_BRANCH" "$GITOPS_REPO_URL" gitops - cd gitops - - echo "Updating prod image.tag to $IMAGE_TAG" - yq -i '.image.tag = strenv(IMAGE_TAG)' "$VALUES_FILE" - - git add "$VALUES_FILE" - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "mas-presensi prod deploy ${IMAGE_TAG}" - git push origin "$GITOPS_BRANCH" \ No newline at end of file + - local: "ci/production.yml" + rules: + - if: '$CI_COMMIT_BRANCH == "production"' From 8a006f377e8b6ab5ee0688ffade3a422caa8bd68 Mon Sep 17 00:00:00 2001 From: M1 AIR Date: Wed, 4 Mar 2026 23:45:06 +0700 Subject: [PATCH 06/12] Update flow environment --- .gitlab-ci.yml | 263 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 236 insertions(+), 27 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2417298f..e027ac2d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,35 +1,244 @@ +stages: + - build + - gitops + +variables: + AWS_REGION: ap-southeast-3 + ECR_REGISTRY: 886436954922.dkr.ecr.ap-southeast-3.amazonaws.com + ECR_REPO_NAME: mbugroup/lti-api + ECR_REPOSITORY: ${ECR_REGISTRY}/${ECR_REPO_NAME} + + DOCKER_HOST: unix:///var/run/docker.sock + DOCKER_TLS_CERTDIR: "" + DOCKER_BUILDKIT: "1" + workflow: rules: - # MR pipeline + # run untuk push & MR + - if: '$CI_PIPELINE_SOURCE == "push"' - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - when: always - - # Push pipeline hanya untuk env branch - - if: '$CI_COMMIT_BRANCH == "development"' - when: always - - if: '$CI_COMMIT_BRANCH == "staging"' - when: always - - if: '$CI_COMMIT_BRANCH == "production"' - when: always - - # Selain itu jangan buat pipeline - when: never -include: - # khusus MR (notif) - - local: "ci/merge_request.yml" - rules: - - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' +# ========================= +# Helper: login ECR +# ========================= +.ecr_login: &ecr_login | + AWS_CLI_ENV_ARGS="" + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_REGION=$AWS_REGION" + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-}" + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-}" + if [ -n "${AWS_SESSION_TOKEN:-}" ]; then + AWS_CLI_ENV_ARGS="$AWS_CLI_ENV_ARGS -e AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN" + fi - # khusus push ke branch env - - local: "ci/development.yml" - rules: - - if: '$CI_COMMIT_BRANCH == "development"' + PASS="$(docker run --rm $AWS_CLI_ENV_ARGS public.ecr.aws/aws-cli/aws-cli:latest \ + ecr get-login-password --region "$AWS_REGION" || true)" + if [ -z "$PASS" ]; then + echo "ERROR: Failed to get ECR login password." + exit 1 + fi + echo "$PASS" | docker login --username AWS --password-stdin "$ECR_REGISTRY" - - local: "ci/staging.yml" - rules: - - if: '$CI_COMMIT_BRANCH == "staging"' +# ========================= +# DEV (push ke development) +# ========================= +build_push_dev: + stage: build + image: public.ecr.aws/docker/library/docker:27 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' + variables: + IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" + before_script: + - set -eu + - docker version + - docker info + - *ecr_login + script: | + set -eu + echo "Build & push: $ECR_REPOSITORY:$IMAGE_TAG" + BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" - - local: "ci/production.yml" - rules: - - if: '$CI_COMMIT_BRANCH == "production"' + n=1 + until [ "$n" -gt 3 ]; do + docker pull "$BASE_IMAGE" && break + echo "Pull base image failed (attempt $n/3), retrying..." + sleep $((n * 10)) + n=$((n + 1)) + done + [ "$n" -le 3 ] || (echo "ERROR: Failed pulling base image: $BASE_IMAGE" && exit 1) + + docker build \ + --build-arg NODE_IMAGE="$BASE_IMAGE" \ + --build-arg NODE_ENV="${NODE_ENV:-dev}" \ + --build-arg PORT="${PORT:-3000}" \ + --build-arg NEXT_PUBLIC_API_BASE_URL="${NEXT_PUBLIC_API_BASE_URL:-/api}" \ + --build-arg DATABASE_URL="${DATABASE_URL:-}" \ + --build-arg AUTH_JWT_SECRET="${AUTH_JWT_SECRET:-}" \ + --build-arg NEXT_PUBLIC_POWERSYNC_URL="${NEXT_PUBLIC_POWERSYNC_URL:-}" \ + --build-arg POWERSYNC_PRIVATE_KEY="${POWERSYNC_PRIVATE_KEY:-}" \ + --build-arg POWERSYNC_PUBLIC_KEY="${POWERSYNC_PUBLIC_KEY:-}" \ + -t "$ECR_REPOSITORY:$IMAGE_TAG" \ + . + + docker push "$ECR_REPOSITORY:$IMAGE_TAG" + +update_gitops_dev_presensi: + stage: gitops + image: public.ecr.aws/docker/library/alpine:3.20 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' + needs: ["build_push_dev"] + variables: + IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" + GITOPS_BRANCH: main + VALUES_FILE: environments/mas-presensi/dev/mas-presensi-values-dev.yaml + GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git + before_script: + - set -eu + - apk add --no-cache git yq + - git config --global user.email "ci@gitlab" + - git config --global user.name "gitlab-ci" + script: | + set -eu + rm -rf gitops + git clone --depth 1 --branch "$GITOPS_BRANCH" "$GITOPS_REPO_URL" gitops + cd gitops + + echo "Updating dev image.tag to $IMAGE_TAG" + yq -i '.image.tag = strenv(IMAGE_TAG)' "$VALUES_FILE" + + git add "$VALUES_FILE" + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "mas-presensi dev deploy ${IMAGE_TAG}" + git push origin "$GITOPS_BRANCH" + +# ========================= +# PROD +# 1) MR dev -> prod (merge_request_event, target production) : build optional push +# 2) Setelah merge (push ke branch production) : build + push + update gitops +# ========================= + +# (A) MR pipeline (validate build dari state MR) +build_prod_mr: + stage: build + image: public.ecr.aws/docker/library/docker:27 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "production"' + variables: + IMAGE_TAG: "prod-mr-${CI_COMMIT_SHORT_SHA}" + # kalau mau push juga saat MR, set di project/CI variable: PUSH_IMAGE=true + PUSH_IMAGE: "${PUSH_IMAGE:-false}" + before_script: + - set -eu + - docker version + - docker info + - *ecr_login + script: | + set -eu + echo "Build (MR) : $ECR_REPOSITORY:$IMAGE_TAG" + BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" + docker pull "$BASE_IMAGE" || true + + docker build \ + --build-arg NODE_IMAGE="$BASE_IMAGE" \ + --build-arg NODE_ENV="${NODE_ENV:-production}" \ + --build-arg PORT="${PORT:-3000}" \ + --build-arg NEXT_PUBLIC_API_BASE_URL="${NEXT_PUBLIC_API_BASE_URL:-/api}" \ + --build-arg DATABASE_URL="${DATABASE_URL:-}" \ + --build-arg AUTH_JWT_SECRET="${AUTH_JWT_SECRET:-}" \ + --build-arg NEXT_PUBLIC_POWERSYNC_URL="${NEXT_PUBLIC_POWERSYNC_URL:-}" \ + --build-arg POWERSYNC_PRIVATE_KEY="${POWERSYNC_PRIVATE_KEY:-}" \ + --build-arg POWERSYNC_PUBLIC_KEY="${POWERSYNC_PUBLIC_KEY:-}" \ + -t "$ECR_REPOSITORY:$IMAGE_TAG" \ + . + + if [ "$PUSH_IMAGE" = "true" ]; then + echo "Pushing image for MR..." + docker push "$ECR_REPOSITORY:$IMAGE_TAG" + else + echo "Skip push (MR)." + fi + +# (B) push ke production (hasil merge) => ini yang "build dari code merge" +build_push_prod: + stage: build + image: public.ecr.aws/docker/library/docker:27 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' + variables: + IMAGE_TAG: "prod-${CI_COMMIT_SHORT_SHA}" + before_script: + - set -eu + - docker version + - docker info + - *ecr_login + script: | + set -eu + echo "Build & push (prod): $ECR_REPOSITORY:$IMAGE_TAG" + BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" + + n=1 + until [ "$n" -gt 3 ]; do + docker pull "$BASE_IMAGE" && break + echo "Pull base image failed (attempt $n/3), retrying..." + sleep $((n * 10)) + n=$((n + 1)) + done + [ "$n" -le 3 ] || (echo "ERROR: Failed pulling base image: $BASE_IMAGE" && exit 1) + + docker build \ + --build-arg NODE_IMAGE="$BASE_IMAGE" \ + --build-arg NODE_ENV="${NODE_ENV:-production}" \ + --build-arg PORT="${PORT:-3000}" \ + --build-arg NEXT_PUBLIC_API_BASE_URL="${NEXT_PUBLIC_API_BASE_URL:-/api}" \ + --build-arg DATABASE_URL="${DATABASE_URL:-}" \ + --build-arg AUTH_JWT_SECRET="${AUTH_JWT_SECRET:-}" \ + --build-arg NEXT_PUBLIC_POWERSYNC_URL="${NEXT_PUBLIC_POWERSYNC_URL:-}" \ + --build-arg POWERSYNC_PRIVATE_KEY="${POWERSYNC_PRIVATE_KEY:-}" \ + --build-arg POWERSYNC_PUBLIC_KEY="${POWERSYNC_PUBLIC_KEY:-}" \ + -t "$ECR_REPOSITORY:$IMAGE_TAG" \ + . + + docker push "$ECR_REPOSITORY:$IMAGE_TAG" + +update_gitops_prod_presensi: + stage: gitops + image: public.ecr.aws/docker/library/alpine:3.20 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' + needs: ["build_push_prod"] + variables: + IMAGE_TAG: "prod-${CI_COMMIT_SHORT_SHA}" + GITOPS_BRANCH: main + VALUES_FILE: environments/lti/prod/lti-values-prod.yaml .yaml + GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git + before_script: + - set -eu + - apk add --no-cache git yq + - git config --global user.email "ci@gitlab" + - git config --global user.name "gitlab-ci" + script: | + set -eu + rm -rf gitops + git clone --depth 1 --branch "$GITOPS_BRANCH" "$GITOPS_REPO_URL" gitops + cd gitops + + echo "Updating prod image.tag to $IMAGE_TAG" + yq -i '.image.tag = strenv(IMAGE_TAG)' "$VALUES_FILE" + + git add "$VALUES_FILE" + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "mas-presensi prod deploy ${IMAGE_TAG}" + git push origin "$GITOPS_BRANCH" From 77ac46a0297c7f9f4145bac838eec8ed8bdc022f Mon Sep 17 00:00:00 2001 From: M1 AIR Date: Thu, 5 Mar 2026 00:04:23 +0700 Subject: [PATCH 07/12] ci: adjust lti gitlab pipeline for ecr gitops --- .gitlab-ci.yml | 138 ++++--------------------------------------------- 1 file changed, 10 insertions(+), 128 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e027ac2d..9e0eb79b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,96 +39,16 @@ workflow: fi echo "$PASS" | docker login --username AWS --password-stdin "$ECR_REGISTRY" -# ========================= -# DEV (push ke development) -# ========================= -build_push_dev: - stage: build - image: public.ecr.aws/docker/library/docker:27 - tags: [self-hosted-dev] - rules: - - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' - variables: - IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" - before_script: - - set -eu - - docker version - - docker info - - *ecr_login - script: | - set -eu - echo "Build & push: $ECR_REPOSITORY:$IMAGE_TAG" - BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" - - n=1 - until [ "$n" -gt 3 ]; do - docker pull "$BASE_IMAGE" && break - echo "Pull base image failed (attempt $n/3), retrying..." - sleep $((n * 10)) - n=$((n + 1)) - done - [ "$n" -le 3 ] || (echo "ERROR: Failed pulling base image: $BASE_IMAGE" && exit 1) - - docker build \ - --build-arg NODE_IMAGE="$BASE_IMAGE" \ - --build-arg NODE_ENV="${NODE_ENV:-dev}" \ - --build-arg PORT="${PORT:-3000}" \ - --build-arg NEXT_PUBLIC_API_BASE_URL="${NEXT_PUBLIC_API_BASE_URL:-/api}" \ - --build-arg DATABASE_URL="${DATABASE_URL:-}" \ - --build-arg AUTH_JWT_SECRET="${AUTH_JWT_SECRET:-}" \ - --build-arg NEXT_PUBLIC_POWERSYNC_URL="${NEXT_PUBLIC_POWERSYNC_URL:-}" \ - --build-arg POWERSYNC_PRIVATE_KEY="${POWERSYNC_PRIVATE_KEY:-}" \ - --build-arg POWERSYNC_PUBLIC_KEY="${POWERSYNC_PUBLIC_KEY:-}" \ - -t "$ECR_REPOSITORY:$IMAGE_TAG" \ - . - - docker push "$ECR_REPOSITORY:$IMAGE_TAG" - -update_gitops_dev_presensi: - stage: gitops - image: public.ecr.aws/docker/library/alpine:3.20 - tags: [self-hosted-dev] - rules: - - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' - needs: ["build_push_dev"] - variables: - IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" - GITOPS_BRANCH: main - VALUES_FILE: environments/mas-presensi/dev/mas-presensi-values-dev.yaml - GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git - before_script: - - set -eu - - apk add --no-cache git yq - - git config --global user.email "ci@gitlab" - - git config --global user.name "gitlab-ci" - script: | - set -eu - rm -rf gitops - git clone --depth 1 --branch "$GITOPS_BRANCH" "$GITOPS_REPO_URL" gitops - cd gitops - - echo "Updating dev image.tag to $IMAGE_TAG" - yq -i '.image.tag = strenv(IMAGE_TAG)' "$VALUES_FILE" - - git add "$VALUES_FILE" - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "mas-presensi dev deploy ${IMAGE_TAG}" - git push origin "$GITOPS_BRANCH" - # ========================= # PROD -# 1) MR dev -> prod (merge_request_event, target production) : build optional push -# 2) Setelah merge (push ke branch production) : build + push + update gitops +# 1) MR ke target production: build (optional push) +# 2) Push ke production: build + push + update gitops # ========================= -# (A) MR pipeline (validate build dari state MR) build_prod_mr: stage: build image: public.ecr.aws/docker/library/docker:27 - tags: [self-hosted-dev] + tags: [self-hosted-prod] rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "production"' variables: @@ -143,21 +63,7 @@ build_prod_mr: script: | set -eu echo "Build (MR) : $ECR_REPOSITORY:$IMAGE_TAG" - BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" - docker pull "$BASE_IMAGE" || true - - docker build \ - --build-arg NODE_IMAGE="$BASE_IMAGE" \ - --build-arg NODE_ENV="${NODE_ENV:-production}" \ - --build-arg PORT="${PORT:-3000}" \ - --build-arg NEXT_PUBLIC_API_BASE_URL="${NEXT_PUBLIC_API_BASE_URL:-/api}" \ - --build-arg DATABASE_URL="${DATABASE_URL:-}" \ - --build-arg AUTH_JWT_SECRET="${AUTH_JWT_SECRET:-}" \ - --build-arg NEXT_PUBLIC_POWERSYNC_URL="${NEXT_PUBLIC_POWERSYNC_URL:-}" \ - --build-arg POWERSYNC_PRIVATE_KEY="${POWERSYNC_PRIVATE_KEY:-}" \ - --build-arg POWERSYNC_PUBLIC_KEY="${POWERSYNC_PUBLIC_KEY:-}" \ - -t "$ECR_REPOSITORY:$IMAGE_TAG" \ - . + docker build -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . if [ "$PUSH_IMAGE" = "true" ]; then echo "Pushing image for MR..." @@ -166,11 +72,10 @@ build_prod_mr: echo "Skip push (MR)." fi -# (B) push ke production (hasil merge) => ini yang "build dari code merge" build_push_prod: stage: build image: public.ecr.aws/docker/library/docker:27 - tags: [self-hosted-dev] + tags: [self-hosted-prod] rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' variables: @@ -183,43 +88,20 @@ build_push_prod: script: | set -eu echo "Build & push (prod): $ECR_REPOSITORY:$IMAGE_TAG" - BASE_IMAGE="${NODE_BASE_IMAGE:-public.ecr.aws/docker/library/node:20-alpine}" - - n=1 - until [ "$n" -gt 3 ]; do - docker pull "$BASE_IMAGE" && break - echo "Pull base image failed (attempt $n/3), retrying..." - sleep $((n * 10)) - n=$((n + 1)) - done - [ "$n" -le 3 ] || (echo "ERROR: Failed pulling base image: $BASE_IMAGE" && exit 1) - - docker build \ - --build-arg NODE_IMAGE="$BASE_IMAGE" \ - --build-arg NODE_ENV="${NODE_ENV:-production}" \ - --build-arg PORT="${PORT:-3000}" \ - --build-arg NEXT_PUBLIC_API_BASE_URL="${NEXT_PUBLIC_API_BASE_URL:-/api}" \ - --build-arg DATABASE_URL="${DATABASE_URL:-}" \ - --build-arg AUTH_JWT_SECRET="${AUTH_JWT_SECRET:-}" \ - --build-arg NEXT_PUBLIC_POWERSYNC_URL="${NEXT_PUBLIC_POWERSYNC_URL:-}" \ - --build-arg POWERSYNC_PRIVATE_KEY="${POWERSYNC_PRIVATE_KEY:-}" \ - --build-arg POWERSYNC_PUBLIC_KEY="${POWERSYNC_PUBLIC_KEY:-}" \ - -t "$ECR_REPOSITORY:$IMAGE_TAG" \ - . - + docker build -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . docker push "$ECR_REPOSITORY:$IMAGE_TAG" -update_gitops_prod_presensi: +update_gitops_prod_lti: stage: gitops image: public.ecr.aws/docker/library/alpine:3.20 - tags: [self-hosted-dev] + tags: [self-hosted-prod] rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' needs: ["build_push_prod"] variables: IMAGE_TAG: "prod-${CI_COMMIT_SHORT_SHA}" GITOPS_BRANCH: main - VALUES_FILE: environments/lti/prod/lti-values-prod.yaml .yaml + VALUES_FILE: environments/lti/prod/lti-values-prod.yaml GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git before_script: - set -eu @@ -240,5 +122,5 @@ update_gitops_prod_presensi: echo "No changes to commit" exit 0 fi - git commit -m "mas-presensi prod deploy ${IMAGE_TAG}" + git commit -m "lti prod deploy ${IMAGE_TAG}" git push origin "$GITOPS_BRANCH" From 54e48784064ab71608115e850bf7d0c666488f20 Mon Sep 17 00:00:00 2001 From: M1 AIR Date: Thu, 5 Mar 2026 00:08:50 +0700 Subject: [PATCH 08/12] ci: use self-hosted-dev runner tags --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9e0eb79b..64735d49 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,7 +48,7 @@ workflow: build_prod_mr: stage: build image: public.ecr.aws/docker/library/docker:27 - tags: [self-hosted-prod] + tags: [self-hosted-dev] rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "production"' variables: @@ -75,7 +75,7 @@ build_prod_mr: build_push_prod: stage: build image: public.ecr.aws/docker/library/docker:27 - tags: [self-hosted-prod] + tags: [self-hosted-dev] rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' variables: @@ -94,7 +94,7 @@ build_push_prod: update_gitops_prod_lti: stage: gitops image: public.ecr.aws/docker/library/alpine:3.20 - tags: [self-hosted-prod] + tags: [self-hosted-dev] rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' needs: ["build_push_prod"] From 562d8a90c88a8a4e58db372801ae525af38caadf Mon Sep 17 00:00:00 2001 From: kris Date: Wed, 4 Mar 2026 17:15:34 +0000 Subject: [PATCH 09/12] Update .gitlab-ci.yml file --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 64735d49..397c15c7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,7 +14,7 @@ variables: workflow: rules: - # run untuk push & MR + # run untuk push & MR ke prod - if: '$CI_PIPELINE_SOURCE == "push"' - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - when: never From 70db3cfd345121647f5f64b0dce07350e655e54f Mon Sep 17 00:00:00 2001 From: M1 AIR Date: Thu, 5 Mar 2026 00:27:05 +0700 Subject: [PATCH 10/12] ci: align mr development production triggers --- .gitlab-ci.yml | 48 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 397c15c7..e97d5455 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,9 +14,10 @@ variables: workflow: rules: - # run untuk push & MR ke prod - - if: '$CI_PIPELINE_SOURCE == "push"' - - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + # run untuk branch utama & MR + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "production"' - when: never # ========================= @@ -40,12 +41,9 @@ workflow: echo "$PASS" | docker login --username AWS --password-stdin "$ECR_REGISTRY" # ========================= -# PROD -# 1) MR ke target production: build (optional push) -# 2) Push ke production: build + push + update gitops +# MR # ========================= - -build_prod_mr: +build_mr: stage: build image: public.ecr.aws/docker/library/docker:27 tags: [self-hosted-dev] @@ -53,8 +51,6 @@ build_prod_mr: - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "production"' variables: IMAGE_TAG: "prod-mr-${CI_COMMIT_SHORT_SHA}" - # kalau mau push juga saat MR, set di project/CI variable: PUSH_IMAGE=true - PUSH_IMAGE: "${PUSH_IMAGE:-false}" before_script: - set -eu - docker version @@ -64,14 +60,34 @@ build_prod_mr: set -eu echo "Build (MR) : $ECR_REPOSITORY:$IMAGE_TAG" docker build -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . + echo "Pushing image for MR..." + docker push "$ECR_REPOSITORY:$IMAGE_TAG" - if [ "$PUSH_IMAGE" = "true" ]; then - echo "Pushing image for MR..." - docker push "$ECR_REPOSITORY:$IMAGE_TAG" - else - echo "Skip push (MR)." - fi +# ========================= +# DEVELOPMENT (push branch development) +# ========================= +build_push_dev: + stage: build + image: public.ecr.aws/docker/library/docker:27 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' + variables: + IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" + before_script: + - set -eu + - docker version + - docker info + - *ecr_login + script: | + set -eu + echo "Build & push (dev): $ECR_REPOSITORY:$IMAGE_TAG" + docker build -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . + docker push "$ECR_REPOSITORY:$IMAGE_TAG" +# ========================= +# PRODUCTION (push branch production) +# ========================= build_push_prod: stage: build image: public.ecr.aws/docker/library/docker:27 From ed1fb1b776a2270f961feedb9ace606dec5bf6bb Mon Sep 17 00:00:00 2001 From: M1 AIR Date: Thu, 5 Mar 2026 00:33:11 +0700 Subject: [PATCH 11/12] ci: add dev gitops trigger after dev build --- .gitlab-ci.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e97d5455..b665db17 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -85,6 +85,40 @@ build_push_dev: docker build -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . docker push "$ECR_REPOSITORY:$IMAGE_TAG" +update_gitops_dev_lti: + stage: gitops + image: public.ecr.aws/docker/library/alpine:3.20 + tags: [self-hosted-dev] + rules: + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "development"' + needs: ["build_push_dev"] + variables: + IMAGE_TAG: "dev-${CI_COMMIT_SHORT_SHA}" + GITOPS_BRANCH: main + VALUES_FILE: environments/lti/dev/lti-values-dev.yaml + GITOPS_REPO_URL: https://oauth2:${GITOPS_TOKEN}@gitlab.com/cristian.anggita.parjaman/gitops.git + before_script: + - set -eu + - apk add --no-cache git yq + - git config --global user.email "ci@gitlab" + - git config --global user.name "gitlab-ci" + script: | + set -eu + rm -rf gitops + git clone --depth 1 --branch "$GITOPS_BRANCH" "$GITOPS_REPO_URL" gitops + cd gitops + + echo "Updating dev image.tag to $IMAGE_TAG" + yq -i '.image.tag = strenv(IMAGE_TAG)' "$VALUES_FILE" + + git add "$VALUES_FILE" + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "lti dev deploy ${IMAGE_TAG}" + git push origin "$GITOPS_BRANCH" + # ========================= # PRODUCTION (push branch production) # ========================= From 735c8e00d0c0e8d27100e85c582dcaa723e8827b Mon Sep 17 00:00:00 2001 From: M1 AIR Date: Thu, 5 Mar 2026 00:39:08 +0700 Subject: [PATCH 12/12] ci: force amd64 docker build platform --- .gitlab-ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b665db17..a11b91e1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,7 @@ variables: ECR_REGISTRY: 886436954922.dkr.ecr.ap-southeast-3.amazonaws.com ECR_REPO_NAME: mbugroup/lti-api ECR_REPOSITORY: ${ECR_REGISTRY}/${ECR_REPO_NAME} + TARGET_PLATFORM: linux/amd64 DOCKER_HOST: unix:///var/run/docker.sock DOCKER_TLS_CERTDIR: "" @@ -59,7 +60,7 @@ build_mr: script: | set -eu echo "Build (MR) : $ECR_REPOSITORY:$IMAGE_TAG" - docker build -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . + docker build --platform "$TARGET_PLATFORM" -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . echo "Pushing image for MR..." docker push "$ECR_REPOSITORY:$IMAGE_TAG" @@ -82,7 +83,7 @@ build_push_dev: script: | set -eu echo "Build & push (dev): $ECR_REPOSITORY:$IMAGE_TAG" - docker build -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . + docker build --platform "$TARGET_PLATFORM" -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . docker push "$ECR_REPOSITORY:$IMAGE_TAG" update_gitops_dev_lti: @@ -138,7 +139,7 @@ build_push_prod: script: | set -eu echo "Build & push (prod): $ECR_REPOSITORY:$IMAGE_TAG" - docker build -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . + docker build --platform "$TARGET_PLATFORM" -f Dockerfile -t "$ECR_REPOSITORY:$IMAGE_TAG" . docker push "$ECR_REPOSITORY:$IMAGE_TAG" update_gitops_prod_lti: