├── README.md ├── LICENSE └── .github └── workflows └── deployment.yml /README.md: -------------------------------------------------------------------------------- 1 | InterSystems Demo Deployment 2 | === 3 | 4 | Usage 5 | == 6 | 7 | In your repository create file `.github/workflows/deploy.yml` with content. Replace `` with the domain name, which will be used before `.demo.community.intersystems.com`. Ask for deployment key and set it to secrets as `SERVICE_ACCOUNT_KEY` 8 | 9 | ```yaml 10 | name: Cloud Run Deploy 11 | 12 | on: 13 | push: 14 | branches: 15 | - master 16 | - main 17 | workflow_dispatch: 18 | 19 | jobs: 20 | deploy: 21 | uses: intersystems-community/demo-deployment/.github/workflows/deployment.yml@master 22 | with: 23 | name: 24 | ## Optional 25 | # memory: 1Gi 26 | # port: 8081 27 | # persistence: true 28 | # namespace: demo 29 | secrets: 30 | SERVICE_ACCOUNT_KEY: ${{ secrets.SERVICE_ACCOUNT_KEY }} 31 | ## Optional 32 | # CUSTOM_VARS_LIST: "var1=${{ secrets.CUSTOM_VAR1 }},var2=value2,..." 33 | ``` 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 InterSystems Developer Community 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/deployment.yml: -------------------------------------------------------------------------------- 1 | name: Cloud Run Deploy 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | name: 7 | description: 'Domain name of deployment, before `.demo.community.intersystems.com`' 8 | required: true 9 | type: string 10 | namespace: 11 | description: 'Namespace to deploy to' 12 | required: false 13 | type: string 14 | default: demo 15 | memory: 16 | description: 'Memory for the instance' 17 | type: string 18 | default: 1Gi 19 | port: 20 | description: 'WebPort for the running instance' 21 | type: number 22 | default: 52773 23 | persistence: 24 | description: 'Set to true to persist your data.' 25 | required: false 26 | type: boolean 27 | default: false 28 | secrets: 29 | SERVICE_ACCOUNT_KEY: 30 | required: true 31 | CUSTOM_VARS_LIST: 32 | required: false 33 | workflow_dispatch: 34 | inputs: 35 | repository: 36 | description: 'Deploying repository' 37 | required: true 38 | type: string 39 | ref: 40 | description: 'Branch Name in deploying repository' 41 | required: true 42 | type: choice 43 | default: main 44 | options: 45 | - master 46 | - main 47 | name: 48 | description: 'Domain name of deployment, before `.demo.community.intersystems.com`' 49 | required: true 50 | type: string 51 | 52 | env: 53 | # Change this section according to your needs 54 | IMAGE_NAME: ${{ inputs.name }} 55 | SERVICE: ${{ inputs.name }} 56 | DOMAIN_NAME: ${{ inputs.name }}.demo.community.intersystems.com 57 | 58 | # Leave this section untouched 59 | PROJECT_ID: iris-community-demos 60 | CLUSTER_NAME: demo 61 | GITHUB_SHA: ${{ github.sha }} 62 | GITHUB_REPO: ${{ github.repository }} 63 | REGION: europe-west2 64 | NAMESPACE: ${{ inputs.namespace }} 65 | SERVICE_PORT: ${{ inputs.port }} 66 | SERVICE_MEMORY: ${{ inputs.memory }} 67 | 68 | jobs: 69 | deploy-cloud-run: 70 | # if: github.event.repository.fork == false && github.event.repository.is_template == false 71 | name: Deploy to Cloud Run 72 | runs-on: ubuntu-22.04 73 | if: ${{ ! inputs.persistence }} 74 | steps: 75 | - name: Checkout 76 | uses: actions/checkout@v4 77 | with: 78 | repository: ${{ inputs.repository }} 79 | ref: ${{ inputs.ref }} 80 | 81 | - name: Google Authentication 82 | uses: google-github-actions/auth@v2.1.11 83 | with: 84 | credentials_json: ${{ secrets.SERVICE_ACCOUNT_KEY }} 85 | 86 | - name: Get GKE credentials 87 | uses: google-github-actions/get-gke-credentials@v2.3.4 88 | with: 89 | project_id: ${{ env.PROJECT_ID }} 90 | cluster_name: ${{ env.CLUSTER_NAME }} 91 | location: ${{ env.REGION }} 92 | 93 | - name: Setup gcloud cli 94 | uses: google-github-actions/setup-gcloud@v2.1.5 95 | with: 96 | version: '512.0.0' 97 | 98 | - name: Authorize Docker push 99 | run: | 100 | gcloud --quiet auth configure-docker ${REGION}-docker.pkg.dev 101 | 102 | - name: Build and Push image 103 | run: | 104 | docker buildx build -t ${REGION}-docker.pkg.dev/${PROJECT_ID}/community/${IMAGE_NAME}:${GITHUB_SHA} --push . 105 | 106 | # Setting verbosity to debug leads to printing custom variables in the pipeline logs. 107 | # It might be possible to replace 'set-env-vars' to 'set-secrets' but it requires additional Service Account permissions. 108 | - name: Deploy to Cloud Run 109 | run: | 110 | echo "[INFO] Set google project..." 111 | gcloud config set project ${PROJECT_ID} 112 | 113 | echo "[INFO] Escaping custom variables..." 114 | export CUSTOM_VARS_LIST_ESCAPED=$(echo "${{ secrets.CUSTOM_VARS_LIST }}" | sed -E 's/"/\\"/g') 115 | 116 | echo "[INFO] Deploy service..." 117 | gcloud run deploy ${SERVICE} \ 118 | --platform gke \ 119 | --cluster ${CLUSTER_NAME} \ 120 | --cluster-location ${REGION} \ 121 | --namespace ${NAMESPACE} \ 122 | --port ${SERVICE_PORT:-52773} \ 123 | --min-instances 1 \ 124 | --memory ${SERVICE_MEMORY:-512Mi} \ 125 | --timeout 300 \ 126 | --verbosity info \ 127 | --set-env-vars GITHUB_REPO=${GITHUB_REPO}${CUSTOM_VARS_LIST_ESCAPED:+,${CUSTOM_VARS_LIST_ESCAPED}} \ 128 | --image ${REGION}-docker.pkg.dev/${PROJECT_ID}/community/${IMAGE_NAME}:${GITHUB_SHA} 129 | 130 | echo "[INFO] Create domain mappings..." 131 | if [[ $(gcloud run domain-mappings list --platform gke --cluster ${CLUSTER_NAME} --cluster-location ${REGION} --namespace ${NAMESPACE} --filter "DOMAIN=${DOMAIN_NAME}" | grep -v DOMAIN | wc -l) == 0 ]]; then 132 | gcloud run domain-mappings create \ 133 | --service ${SERVICE} \ 134 | --platform gke \ 135 | --cluster ${CLUSTER_NAME} \ 136 | --cluster-location ${REGION} \ 137 | --namespace ${NAMESPACE} \ 138 | --verbosity debug \ 139 | --domain ${DOMAIN_NAME} 140 | fi 141 | 142 | - name: Create domain name 143 | run: | 144 | kubectl version 145 | echo "[INFO] Checking if [${DOMAIN_NAME}] is in the existing Ingress annotation..." 146 | CURRENT_DOMAINS_LIST=$(kubectl -n gke-system get svc istio-ingress -o jsonpath="{.metadata.annotations['external-dns\.alpha\.kubernetes\.io/hostname']}") 147 | if [[ $(echo ${CURRENT_DOMAINS_LIST} | grep -w "${DOMAIN_NAME}" | wc -c) -eq 0 ]]; then \ 148 | echo "[INFO] Domain [${DOMAIN_NAME}] is ABSENT in the domains list. Adding..."; \ 149 | kubectl -n gke-system annotate --overwrite svc istio-ingress external-dns\.alpha\.kubernetes\.io/hostname=${CURRENT_DOMAINS_LIST},${DOMAIN_NAME}; \ 150 | echo -n "[INFO] Resulting domain names: " 151 | kubectl -n gke-system get svc istio-ingress -o jsonpath="{.metadata.annotations['external-dns\.alpha\.kubernetes\.io/hostname']}" 152 | else 153 | echo "[INFO] Domain [${DOMAIN_NAME}] is in the domains list. Leave untouched..."; \ 154 | fi 155 | 156 | - name: Enable TLS-access 157 | run: | 158 | kubectl version 159 | kubectl patch configmap config-domainmapping -n knative-serving -p '{"data":{"autoTLS":"Enabled"}}' 160 | 161 | deploy-stateful-workload: 162 | name: Deploy Stateful Workload 163 | runs-on: ubuntu-22.04 164 | if: ${{ inputs.persistence }} 165 | steps: 166 | - name: Checkout 167 | uses: actions/checkout@v4 168 | with: 169 | repository: ${{ inputs.repository }} 170 | ref: ${{ inputs.ref }} 171 | 172 | - name: Google Authentication 173 | uses: google-github-actions/auth@v2.1.11 174 | with: 175 | credentials_json: ${{ secrets.SERVICE_ACCOUNT_KEY }} 176 | 177 | - name: Get GKE credentials 178 | uses: google-github-actions/get-gke-credentials@v2.3.4 179 | with: 180 | project_id: ${{ env.PROJECT_ID }} 181 | cluster_name: ${{ env.CLUSTER_NAME }} 182 | location: ${{ env.REGION }} 183 | 184 | - name: Setup gcloud cli 185 | uses: google-github-actions/setup-gcloud@v2.1.5 186 | with: 187 | version: '512.0.0' 188 | 189 | - name: Authorize Docker push 190 | run: | 191 | gcloud --quiet auth configure-docker ${REGION}-docker.pkg.dev 192 | 193 | - name: Build and Push image 194 | run: | 195 | docker buildx build -t ${REGION}-docker.pkg.dev/${PROJECT_ID}/community/${IMAGE_NAME}:${GITHUB_SHA} --push . 196 | 197 | - name: Prepare Helm Environment Variables 198 | id: prepare_helm_vars 199 | run: | 200 | echo "[INFO] Escaping custom variables..." 201 | export CUSTOM_VARS_LIST_ESCAPED=$(echo "${{ secrets.CUSTOM_VARS_LIST }}" | sed -E 's/"/\\"/g') 202 | 203 | HELM_ARGS_STRING="" 204 | if [[ -n "$CUSTOM_VARS_LIST_ESCAPED" ]]; then 205 | echo "[INFO] CUSTOM_VARS_LIST_ESCAPED contains data. Parsing key-values..." 206 | IFS=',' read -r -a ENV_PAIRS <<< "$CUSTOM_VARS_LIST_ESCAPED" 207 | 208 | INDEX=2 209 | for PAIR in "${ENV_PAIRS[@]}"; do 210 | # Skip empty pairs 211 | if [[ -z "$PAIR" ]]; then 212 | continue 213 | fi 214 | 215 | # Check if the pair contains '=' 216 | if [[ "$PAIR" != *"="* ]]; then 217 | echo "[WARNING] Skipping malformed pair (no '='): '$PAIR'" 218 | continue 219 | fi 220 | 221 | # Split each pair into KEY and VALUE at the first '=' 222 | KEY="${PAIR%%=*}" 223 | VALUE="${PAIR#*=}" 224 | echo "[INFO] Processing pair: '$PAIR' -> KEY='$KEY', VALUE='$VALUE'" 225 | 226 | # Only add if KEY is not empty (VALUE can be empty string) 227 | if [[ -n "$KEY" ]]; then 228 | HELM_ARGS_STRING="$HELM_ARGS_STRING --set-string 'extraEnv[$INDEX].name=$KEY' --set-string 'extraEnv[$INDEX].value=$VALUE'" 229 | ((INDEX++)) 230 | else 231 | echo "[WARNING] Skipping empty key: KEY='$KEY', VALUE='$VALUE'" 232 | fi 233 | done 234 | fi 235 | 236 | # Output the helm args string 237 | echo "helm_args=$HELM_ARGS_STRING" >> $GITHUB_OUTPUT 238 | echo "[DEBUG] Generated helm_args: $HELM_ARGS_STRING" 239 | 240 | - name: Deploy Stateful Workload 241 | run: | 242 | echo "[INFO] Set google project..." 243 | gcloud config set project ${PROJECT_ID} 244 | 245 | echo "[INFO] Escaping custom variables..." 246 | export CUSTOM_VARS_LIST_ESCAPED=$(echo "${{ secrets.CUSTOM_VARS_LIST }}" | sed -E 's/"/\\"/g') 247 | 248 | echo "[INFO] Installing IRIS Helm charts repository..." 249 | helm repo add intersystems-charts https://charts.demo.community.intersystems.com 250 | 251 | echo "[INFO] Deploy Helm release..." 252 | helm upgrade --install ${{ inputs.name }} intersystems-charts/iris-app \ 253 | --namespace ${NAMESPACE} \ 254 | --version 0.0.1 \ 255 | --set image.repository=${REGION}-docker.pkg.dev/${PROJECT_ID}/community/${IMAGE_NAME} \ 256 | --set image.tag=${GITHUB_SHA} \ 257 | --set resources.limits.memory=${SERVICE_MEMORY:-1Gi} \ 258 | --set service.webPort=${SERVICE_PORT:-52773} \ 259 | --set ingress.name=${{ inputs.name }} \ 260 | --set ingress.domain=${DOMAIN_NAME} \ 261 | --set-string 'extraEnv[0].name=ISC_DATA_DIRECTORY' \ 262 | --set-string 'extraEnv[0].value=/isc/data' \ 263 | --set-string 'extraEnv[1].name=GITHUB_REPO' \ 264 | --set-string 'extraEnv[1].value=${GITHUB_REPO}' \ 265 | ${{ steps.prepare_helm_vars.outputs.helm_args }} \ 266 | --wait \ 267 | --atomic \ 268 | --timeout 10m 269 | --------------------------------------------------------------------------------