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" 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