diff --git a/.github/workflows/argocd-create-app.yml b/.github/workflows/argocd-create-app.yml new file mode 100644 index 00000000..f7233e82 --- /dev/null +++ b/.github/workflows/argocd-create-app.yml @@ -0,0 +1,187 @@ +name: "ArgoCD: Create Application" + +on: + workflow_call: + inputs: + docker_image_version: + required: true + type: string + branch_name: + required: true + type: string + workflow_dispatch: + inputs: + docker_image_version: + description: 'Docker image version' + required: true + type: string + branch_name: + description: 'Branch name' + required: true + type: string + +env: + INFRA_REPO: "HumanSignal/infra" + APP_BASE_NAME: "adala" + NAMESPACE: "prompt" + DOMAIN: "dev.heartex.com" + TEMPLATE_DIR: "vars/aws/dev.heartex.com/k8s/prompt/templates/adala" + APPS_DIR: "vars/aws/dev.heartex.com/k8s/prompt" + DOCKER_IMAGE_VALUES_YAML_KEY: ".app.deployment.image.tag" + REPLICA_COUNT_VALUES_YAML_KEY: ".app.deployment.replicaCount" + +jobs: + + deploy: + timeout-minutes: 15 + runs-on: ubuntu-latest + steps: + - uses: hmarr/debug-action@v3.0.0 + + - name: "GitHub deployment: Start" + id: github-deployment-start + uses: bobheadxi/deployments@v1 + with: + step: start + token: ${{ secrets.GIT_PAT }} + env: ${{ inputs.branch_name }} + ref: ${{ inputs.branch_name }} + + - name: Get GitHub user details + id: get-github-user + uses: actions/github-script@v7 + env: + ACTOR_USERNAME: ${{ github.event.sender.login }} + with: + github-token: ${{ secrets.GIT_PAT }} + script: | + const actor_username = process.env.ACTOR_USERNAME; + + let user_name = 'robot-ci-heartex'; + let user_email = 'robot-ci-heartex@users.noreply.github.com'; + + try { + const {data: user} = await github.rest.users.getByUsername({ + username: actor_username, + }); + user_name = user.login; + user_email = user.email; + } catch (e) { + console.log(e) + } + + core.setOutput('user_name', user_name); + core.setOutput('user_email', user_email); + + - name: Configure git + shell: bash + run: | + set -xeuo pipefail + git config --global user.name '${{ steps.get-github-user.outputs.user_name }}' + git config --global user.email '${{ steps.get-github-user.outputs.user_email }}' + + - name: Checkout + uses: actions/checkout@v4 + with: + repository: ${{ env.INFRA_REPO }} + token: ${{ secrets.GIT_PAT }} + fetch-depth: 1 + + - name: Commit + id: commit + shell: bash + env: + DOCKER_IMAGE_VERSION: ${{ inputs.docker_image_version }} + BRANCH_NAME: ${{ inputs.branch_name }} + run: | + set -xeuo pipefail + + pretty_branch_name="$(echo -n "${BRANCH_NAME#refs/heads/}" | sed 's#/#-#g' | sed 's#_#-#g'| sed 's#\.#-#g' | tr '[:upper:]' '[:lower:]')" + + APP_NAME="${pretty_branch_name}-${APP_BASE_NAME}" + APP_DIR="${APPS_DIR}/${APP_NAME}" + APP_VALUES_FILE="${APP_DIR}/values.yaml" + + export DOCKER_IMAGE_TAG="${DOCKER_IMAGE_VERSION}" + echo "DOCKER_IMAGE_TAG=${DOCKER_IMAGE_TAG}" >> $GITHUB_OUTPUT + export HOSTNAME="${pretty_branch_name}-${APP_BASE_NAME}.${NAMESPACE}.${DOMAIN}" + echo "HOSTNAME=${HOSTNAME}" >> $GITHUB_OUTPUT + export RELEASE_NAME="${pretty_branch_name}" + echo "RELEASE_NAME=${RELEASE_NAME}" >> $GITHUB_OUTPUT + + if [ -f "${APP_VALUES_FILE}" ]; then + yq e --inplace "${DOCKER_IMAGE_VALUES_YAML_KEY} |= \"${DOCKER_IMAGE_TAG}\"" "${APP_VALUES_FILE}" + else + mkdir -p "${APP_DIR}" + for template_file_name in $(ls "${TEMPLATE_DIR}"); do + file_name="${template_file_name%.template}" + envsubst < "${TEMPLATE_DIR}/${template_file_name}" > "${APP_DIR}/${file_name}" + done + fi + + yq e --inplace "${REPLICA_COUNT_VALUES_YAML_KEY} |= 1" "${APP_VALUES_FILE}" + + git add "${APP_DIR}" + git status + git commit -m "${DOMAIN}: Create ${APP_NAME}@${NAMESPACE}" -m 'Workflow run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' + + - name: Push + id: push + shell: bash + run: | + set -xeuo pipefail + + attempt=0 + max_attempts=5 + while ! git push; do + attempt=$((attempt+1)) + if [ $attempt -ge $max_attempts ]; then + echo "Max attempts reached. Exiting." + exit 1 + fi + sleep_time=$((RANDOM % 10 + 1)) + echo "Push failed. Attempting retry (${attempt}/${max_attempts}) in ${sleep_time} seconds..." + sleep $sleep_time + git pull --rebase + done + + echo "commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - name: Comment Pull Request + uses: actions/github-script@v7 + env: + PR_NUMBER: ${{ github.event.number }} + COMMIT_SHA: ${{ steps.push.outputs.commit_sha }} + with: + github-token: ${{ secrets.GIT_PAT }} + script: | + const { repo, owner } = context.repo; + const [infraOwner, infraRepo] = process.env.INFRA_REPO.split('/'); + const prNumber = process.env.PR_NUMBER; + const commit_sha = process.env.COMMIT_SHA; + + if (prNumber && commit_sha) { + const { data: commit } = await github.rest.git.getCommit({ + owner: infraOwner, + repo: infraRepo, + commit_sha: commit_sha, + }); + await github.rest.issues.createComment({ + owner: owner, + repo: repo, + issue_number: prNumber, + body: `Commit ${commit.html_url} is created.`, + }); + } + + - name: "GitHub deployment: Finish" + id: github-deployment-finish + if: always() + uses: bobheadxi/deployments@v1 + with: + step: finish + token: ${{ secrets.GIT_PAT }} + status: ${{ job.status }} + deployment_id: ${{ steps.github-deployment-start.outputs.deployment_id }} + env: ${{ steps.github-deployment-start.outputs.env }} + env_url: "https://${{ steps.commit.outputs.HOSTNAME }}" diff --git a/.github/workflows/argocd-delete-app.yml b/.github/workflows/argocd-delete-app.yml new file mode 100644 index 00000000..73a2adcf --- /dev/null +++ b/.github/workflows/argocd-delete-app.yml @@ -0,0 +1,151 @@ +name: "ArgoCD: Delete Application" + +on: + workflow_call: + inputs: + branch_name: + required: true + type: string + workflow_dispatch: + inputs: + branch_name: + description: 'Branch name' + required: true + type: string + pull_request_target: + types: + - closed + branches: + - master + - '**' + + +env: + INFRA_REPO: "HumanSignal/infra" + APP_BASE_NAME: "adala" + NAMESPACE: "prompt" + DOMAIN: "dev.heartex.com" + TEMPLATE_DIR: "vars/aws/dev.heartex.com/k8s/prompt/templates/adala" + APPS_DIR: "vars/aws/dev.heartex.com/k8s/prompt" + +jobs: + + destroy: + timeout-minutes: 15 + runs-on: ubuntu-latest + steps: + - uses: hmarr/debug-action@v3.0.0 + + - name: Get GitHub user details + id: get-github-user + uses: actions/github-script@v7 + env: + ACTOR_USERNAME: ${{ github.event.sender.login }} + with: + github-token: ${{ secrets.GIT_PAT }} + script: | + const actor_username = process.env.ACTOR_USERNAME; + + let user_name = 'robot-ci-heartex'; + let user_email = 'robot-ci-heartex@users.noreply.github.com'; + + try { + const {data: user} = await github.rest.users.getByUsername({ + username: actor_username, + }); + user_name = user.login; + user_email = user.email; + } catch (e) { + console.log(e) + } + + core.setOutput('user_name', user_name); + core.setOutput('user_email', user_email); + + - name: Configure git + shell: bash + run: | + set -xeuo pipefail + git config --global user.name '${{ steps.get-github-user.outputs.user_name }}' + git config --global user.email '${{ steps.get-github-user.outputs.user_email }}' + + - name: Checkout + uses: actions/checkout@v4 + with: + repository: ${{ env.INFRA_REPO }} + token: ${{ secrets.GIT_PAT }} + fetch-depth: 1 + + - name: Commit + shell: bash + env: + DOCKER_IMAGE_VERSION: ${{ inputs.docker_image_version }} + BRANCH_NAME: ${{ inputs.branch_name || github.event.pull_request.head.ref || github.ref_name }} + run: | + set -xeuo pipefail + + pretty_branch_name="$(echo -n "${BRANCH_NAME#refs/heads/}" | sed 's#/#-#g' | sed 's#_#-#g'| sed 's#\.#-#g' | tr '[:upper:]' '[:lower:]')" + + APP_NAME="${pretty_branch_name}-${APP_BASE_NAME}" + APP_DIR="${APPS_DIR}/${APP_NAME}" + + git rm -r "${APP_DIR}" + git status + git commit -m "${DOMAIN}: Delete ${APP_NAME}@${NAMESPACE}" -m 'Workflow run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' + + - name: Push + id: push + shell: bash + run: | + set -xeuo pipefail + + attempt=0 + max_attempts=5 + while ! git push; do + attempt=$((attempt+1)) + if [ $attempt -ge $max_attempts ]; then + echo "Max attempts reached. Exiting." + exit 1 + fi + sleep_time=$((RANDOM % 10 + 1)) + echo "Push failed. Attempting retry (${attempt}/${max_attempts}) in ${sleep_time} seconds..." + sleep $sleep_time + git pull --rebase + done + + echo "commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - name: Comment Pull Request + uses: actions/github-script@v7 + env: + PR_NUMBER: ${{ github.event.number }} + COMMIT_SHA: ${{ steps.push.outputs.commit_sha }} + with: + github-token: ${{ secrets.GIT_PAT }} + script: | + const { repo, owner } = context.repo; + const [infraOwner, infraRepo] = process.env.INFRA_REPO.split('/'); + const prNumber = process.env.PR_NUMBER; + const commit_sha = process.env.COMMIT_SHA; + + if (prNumber && commit_sha) { + const { data: commit } = await github.rest.git.getCommit({ + owner: infraOwner, + repo: infraRepo, + commit_sha: commit_sha, + }); + await github.rest.issues.createComment({ + owner: owner, + repo: repo, + issue_number: prNumber, + body: `Commit ${commit.html_url} is created.`, + }); + } + + - name: "GitHub deployment: Delete" + id: github-deployment-delete + uses: bobheadxi/deployments@v1 + with: + step: delete-env + token: ${{ secrets.GIT_PAT }} + env: ${{ inputs.branch_name || github.event.pull_request.head.ref || github.ref_name }} diff --git a/.github/workflows/argocd-scale-app.yml b/.github/workflows/argocd-scale-app.yml new file mode 100644 index 00000000..ec793568 --- /dev/null +++ b/.github/workflows/argocd-scale-app.yml @@ -0,0 +1,166 @@ +name: "ArgoCD: Scale Application" + +on: + workflow_call: + inputs: + replicas: + required: true + type: string + branch_name: + required: true + type: string + workflow_dispatch: + inputs: + replicas: + description: 'Replicas' + required: true + type: string + branch_name: + description: 'Branch name' + required: true + type: string + pull_request_target: + types: + - converted_to_draft + branches: + - master + - '**' + +env: + INFRA_REPO: "HumanSignal/infra" + APP_BASE_NAME: "adala" + NAMESPACE: "prompt" + DOMAIN: "dev.heartex.com" + TEMPLATE_DIR: "vars/aws/dev.heartex.com/k8s/prompt/templates/adala" + APPS_DIR: "vars/aws/dev.heartex.com/k8s/prompt" + REPLICA_COUNT_VALUES_YAML_KEY: ".app.deployment.replicaCount" + +jobs: + + scale: + timeout-minutes: 15 + runs-on: ubuntu-latest + steps: + - uses: hmarr/debug-action@v3.0.0 + + - name: Get GitHub user details + id: get-github-user + uses: actions/github-script@v7 + env: + ACTOR_USERNAME: ${{ github.event.sender.login }} + with: + github-token: ${{ secrets.GIT_PAT }} + script: | + const actor_username = process.env.ACTOR_USERNAME; + + let user_name = 'robot-ci-heartex'; + let user_email = 'robot-ci-heartex@users.noreply.github.com'; + + try { + const {data: user} = await github.rest.users.getByUsername({ + username: actor_username, + }); + user_name = user.login; + user_email = user.email; + } catch (e) { + console.log(e) + } + + core.setOutput('user_name', user_name); + core.setOutput('user_email', user_email); + + - name: Configure git + shell: bash + run: | + set -xeuo pipefail + git config --global user.name '${{ steps.get-github-user.outputs.user_name }}' + git config --global user.email '${{ steps.get-github-user.outputs.user_email }}' + + - name: Checkout + uses: actions/checkout@v4 + with: + repository: ${{ env.INFRA_REPO }} + token: ${{ secrets.GIT_PAT }} + fetch-depth: 1 + + - name: Commit + shell: bash + env: + REPLICA_COUNT: ${{ inputs.replicas }} + BRANCH_NAME: ${{ inputs.branch_name || github.event.pull_request.head.ref || github.ref_name }} + run: | + set -xeuo pipefail + + pretty_branch_name="$(echo -n "${BRANCH_NAME#refs/heads/}" | sed 's#/#-#g' | sed 's#_#-#g'| sed 's#\.#-#g' | tr '[:upper:]' '[:lower:]')" + + APP_NAME="${pretty_branch_name}-${APP_BASE_NAME}" + APP_DIR="${APPS_DIR}/${APP_NAME}" + APP_VALUES_FILE="${APP_DIR}/values.yaml" + + if [[ "${{ github.event.action }}" = "converted_to_draft" ]]; then + REPLICA_COUNT=0 + fi + + yq e --inplace "${REPLICA_COUNT_VALUES_YAML_KEY} |= ${REPLICA_COUNT}" "${APP_VALUES_FILE}" + + git add "${APP_DIR}" + git status + git commit -m "${DOMAIN}: Downscale ${APP_NAME}@${NAMESPACE} to 0" -m 'Workflow run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' + + - name: Push + id: push + shell: bash + run: | + set -xeuo pipefail + + attempt=0 + max_attempts=5 + while ! git push; do + attempt=$((attempt+1)) + if [ $attempt -ge $max_attempts ]; then + echo "Max attempts reached. Exiting." + exit 1 + fi + sleep_time=$((RANDOM % 10 + 1)) + echo "Push failed. Attempting retry (${attempt}/${max_attempts}) in ${sleep_time} seconds..." + sleep $sleep_time + git pull --rebase + done + + echo "commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - name: Comment Pull Request + uses: actions/github-script@v7 + env: + PR_NUMBER: ${{ github.event.number }} + COMMIT_SHA: ${{ steps.push.outputs.commit_sha }} + with: + github-token: ${{ secrets.GIT_PAT }} + script: | + const { repo, owner } = context.repo; + const [infraOwner, infraRepo] = process.env.INFRA_REPO.split('/'); + const prNumber = process.env.PR_NUMBER; + const commit_sha = process.env.COMMIT_SHA; + + if (prNumber && commit_sha) { + const { data: commit } = await github.rest.git.getCommit({ + owner: infraOwner, + repo: infraRepo, + commit_sha: commit_sha, + }); + await github.rest.issues.createComment({ + owner: owner, + repo: repo, + issue_number: prNumber, + body: `Commit ${commit.html_url} is created.`, + }); + } + + - name: "GitHub deployment: Deactivate" + id: github-deployment-deactivate + uses: bobheadxi/deployments@v1 + with: + step: deactivate-env + token: ${{ secrets.GIT_PAT }} + env: ${{ inputs.branch_name || github.event.pull_request.head.ref || github.ref_name }} + desc: "Environment was scaled down to 0" diff --git a/.github/workflows/cicd-pipeline.yml b/.github/workflows/cicd-pipeline.yml index fb986f3e..2e669ffc 100644 --- a/.github/workflows/cicd-pipeline.yml +++ b/.github/workflows/cicd-pipeline.yml @@ -29,3 +29,14 @@ jobs: sha: ${{ github.event.pull_request.head.sha || github.event.after }} branch_name: ${{ github.event.pull_request.head.ref || github.ref_name }} secrets: inherit + + deploy: + name: "Deploy" + if: github.event_name == 'pull_request_target' + uses: ./.github/workflows/argocd-create-app.yml + needs: + - build + with: + docker_image_version: ${{ needs.build.outputs.image_version }} + branch_name: ${{ github.event.pull_request.head.ref || github.ref_name }} + secrets: inherit