diff --git a/.github/actions/argocd-deployment/checkout-repositories/action.yaml b/.github/actions/argocd-deployment/checkout-repositories/action.yaml index 5b081847..c887683f 100644 --- a/.github/actions/argocd-deployment/checkout-repositories/action.yaml +++ b/.github/actions/argocd-deployment/checkout-repositories/action.yaml @@ -88,6 +88,16 @@ runs: echo "ENV_TO_DEPLOY=${{ github.ref_name }}" >> $GITHUB_ENV fi + elif [[ "${{ github.event_name }}" == "workflow_run" ]]; then + BRANCH_NAME="${{ github.ref_name }}" + echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV + + if [[ "${{ github.ref }}" == "refs/heads/master" || "${{ github.ref }}" == "refs/heads/main" ]]; then + echo "ENV_TO_DEPLOY=prod" >> $GITHUB_ENV + else + echo "ENV_TO_DEPLOY=${{ github.ref_name }}" >> $GITHUB_ENV + fi + else echo "# 🚨 Checkout failed" > $GITHUB_STEP_SUMMARY echo "## Reason" >> $GITHUB_STEP_SUMMARY @@ -128,7 +138,7 @@ runs: shell: bash - uses: actions/checkout@v5 - if: github.event_name == 'push' && + if: (github.event_name == 'push' || github.event_name == 'workflow_run' ) && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main') && steps.set-outputs.outputs.env_to_deploy == 'prod' && (steps.set-outputs.outputs.branch_name == 'master' || steps.set-outputs.outputs.branch_name == 'main') diff --git a/.github/actions/google-cloud-platform/gcs/download-file/action.yaml b/.github/actions/google-cloud-platform/gcs/download-file/action.yaml new file mode 100644 index 00000000..8370b740 --- /dev/null +++ b/.github/actions/google-cloud-platform/gcs/download-file/action.yaml @@ -0,0 +1,84 @@ +--- +name: "GCS download file" +description: "Download a file from Google Cloud Storage." + +inputs: + gcs_bucket: + description: "The GCS bucket that contains the file. (e.g. `bucket-name`)" + required: true + gcs_bucket_path: + description: "The GCS bucket path that contains the file. (e.g `path/to/file`)" + required: false + default: "" + gcs_file_name: + description: "The name of the file to download. (if not provided, the latest file will be downloaded)" + required: false + default: "" + local_path: + description: "The local path to download the file to. (e.g. `./tmp/path/to/file`)" + required: false + default: "." + +outputs: + downloaded_file_location: + description: "The location of the downloaded file." + value: "${{ steps.gcs-download-file.outputs.downloaded_file_location }}" + downloaded_file_name: + description: "The name of the downloaded file." + value: "${{ steps.gcs-download-file.outputs.downloaded_file_name }}" + +runs: + using: "composite" + steps: + - id: gcs-get-latest-file + if: inputs.gcs_file_name == '' + name: Get latest file from GCS + run: | + set -eo pipefail + + if ! LATEST_FILE=$(gcloud storage ls gs://${{ inputs.gcs_bucket }}/${{ inputs.gcs_bucket_path }} | sort -k2 -r | head -n1); then + echo "ERROR: Failed to list GCS bucket contents" + exit 1 + fi + + if [ -z "$LATEST_FILE" ]; then + echo "ERROR: No files found in gs://${{ inputs.gcs_bucket }}/${{ inputs.gcs_bucket_path }}" + exit 1 + fi + + echo "✓ Latest GCS file: $LATEST_FILE" + echo "latest_file_name=$(basename "$LATEST_FILE")" >> $GITHUB_ENV + shell: bash + + - id: gcs-download-file + name: Download file from GCS + run: | + set -eo pipefail + + if [[ ! -z "${{ inputs.gcs_file_name }}" ]]; then + echo "INFO: gcs_file_name provided: ${{ inputs.gcs_file_name }}" + FILE_NAME=${{ inputs.gcs_file_name }} + elif [[ ! -z "${{ env.latest_file_name }}" ]]; then + echo "INFO: No gcs_file_name provided, using latest file: ${{ env.latest_file_name }}" + FILE_NAME=${{ env.latest_file_name }} + else + echo "ERROR: No gcs_file_name provided and no latest file found." + exit 1 + fi + + mkdir -p "${{ inputs.local_path }}" + echo "Downloading file ${FILE_NAME} from GCS ${{ inputs.gcs_bucket }} to ${{ inputs.local_path }}..." + if ! gcloud storage cp "gs://${{ inputs.gcs_bucket }}/${{ inputs.gcs_bucket_path }}/${FILE_NAME}" "${{ inputs.local_path }}/${FILE_NAME}"; then + echo "ERROR: Failed to download file from GCS" + exit 1 + fi + + if [ ! -f "${{ inputs.local_path }}/${FILE_NAME}" ]; then + echo "ERROR: Downloaded file not found at ${{ inputs.local_path }}/${FILE_NAME}" + exit 1 + fi + + echo "✓ Dump downloaded successfully" + echo "downloaded_file_location=${{ inputs.local_path }}" >> $GITHUB_OUTPUT + echo "downloaded_file_name=${FILE_NAME}" >> $GITHUB_OUTPUT + shell: bash diff --git a/.github/actions/kubernetes/exec-cronjob/action.yaml b/.github/actions/kubernetes/exec-cronjob/action.yaml new file mode 100644 index 00000000..23d37922 --- /dev/null +++ b/.github/actions/kubernetes/exec-cronjob/action.yaml @@ -0,0 +1,50 @@ +name: Execute CronJob +description: "Create a Job from a CronJob and wait for it to complete" + +inputs: + cronjob-name: + description: "The name of the CronJob" + required: true + namespace: + description: "The namespace where the CronJob is located" + required: true + timeout: + description: "The timeout to wait for the job execution to complete (default: `600s` for 10 minutes)" + required: false + default: "600s" + +runs: + using: composite + steps: + - id: execute-cronjob + name: Execute CronJob + env: + CRONJOB_NAME: ${{ inputs.cronjob-name }} + NAMESPACE: ${{ inputs.namespace }} + TIMEOUT: ${{ inputs.timeout }} + run: | + set -eo pipefail + + DATE=$(date +%Y%m%d%H%M%S) + JOB_NAME="${{ env.CRONJOB_NAME }}-manual-${DATE}" + + echo "Creating job $JOB_NAME from cronjob/${{ env.CRONJOB_NAME }}..." + if ! kubectl create job --from=cronjob/${{ env.CRONJOB_NAME }} $JOB_NAME -n ${{ env.NAMESPACE }}; then + echo "ERROR: Failed to create job from CronJob" + exit 1 + fi + + echo "Waiting for job $JOB_NAME to complete (timeout: ${{ env.TIMEOUT }})..." + if ! kubectl wait --for=condition=complete job/$JOB_NAME -n ${{ env.NAMESPACE }} --timeout=${{ env.TIMEOUT }}; then + echo "ERROR: Job did not complete within timeout" + exit 1 + fi + + SUCCEEDED=$(kubectl get job $JOB_NAME -n ${{ env.NAMESPACE }} -o jsonpath='{.status.succeeded}') + if [ "$SUCCEEDED" = "1" ]; then + echo "✓ Job $JOB_NAME completed successfully" + else + echo "ERROR: Job $JOB_NAME execution failed" + exit 1 + fi + shell: bash \ No newline at end of file diff --git a/.github/actions/kubernetes/wait-for-deployment/action.yaml b/.github/actions/kubernetes/wait-for-deployment/action.yaml new file mode 100644 index 00000000..20aa25ea --- /dev/null +++ b/.github/actions/kubernetes/wait-for-deployment/action.yaml @@ -0,0 +1,43 @@ +name: Wait for Deployment +description: "Wait for a Kubernetes deployment to be ready" + +inputs: + deployment-label: + description: "The label to use to identify the deployment. (e.g. `key=value`)" + required: true + namespace: + description: "The namespace to wait for the deployment in" + required: true + timeout: + description: "The timeout to wait for the deployment to be ready (default: `600s` for 10 minutes)" + required: false + default: "600s" + +runs: + using: composite + steps: + - id: wait-for-deployment + name: Wait for deployment + env: + DEPLOYMENT_LABEL: ${{ inputs.deployment-label }} + NAMESPACE: ${{ inputs.namespace }} + TIMEOUT: ${{ inputs.timeout }} + run: | + set -eo pipefail + + DEPLOYMENTS=$(kubectl get deployment -l ${{ env.DEPLOYMENT_LABEL }} -n ${{ env.NAMESPACE }} -o jsonpath='{.items[*].metadata.name}') + if [ -z "$DEPLOYMENTS" ]; then + echo "ERROR: No deployments found with label ${{ env.DEPLOYMENT_LABEL }} in namespace ${{ env.NAMESPACE }}" + exit 1 + fi + + for deployment in $DEPLOYMENTS; do + echo "Waiting for deployment/$deployment to be ready..." + if ! kubectl rollout status deployment/$deployment -n ${{ env.NAMESPACE }} --timeout ${{ env.TIMEOUT }}; then + echo "ERROR: Deployment $deployment failed to roll out within timeout period" + exit 1 + fi + echo "✓ Deployment $deployment is ready." + done + echo "✓ All deployments are ready." + shell: bash \ No newline at end of file