API 적용 최초 배포
Deploy feedback demo / deploy (push) Failing after 4m26s

This commit is contained in:
root
2026-09-01 17:12:46 +09:00
parent badf5f2c81
commit aaddfc7bfc
44 changed files with 1331 additions and 867 deletions
+85 -209
View File
@@ -1,6 +1,6 @@
name: Deploy staging
name: Deploy feedback demo
run-name: Deploy staging from main
run-name: Deploy feedback demo from main
on:
push:
@@ -22,143 +22,83 @@ jobs:
STAGING_HOST: ${{ vars.STAGING_HOST }}
STAGING_PORT: ${{ vars.STAGING_PORT }}
STAGING_APP_DIR: ${{ vars.STAGING_APP_DIR }}
STAGING_COMPOSE_FILE: ${{ vars.STAGING_COMPOSE_FILE }}
WEB_PORT: ${{ vars.WEB_PORT }}
SSO_CLIENT_ID_VAR: ${{ vars.SSO_CLIENT_ID }}
SSO_CLIENT_ID_SECRET: ${{ secrets.SSO_CLIENT_ID }}
STAGING_USER: ${{ secrets.STAGING_USER }}
STAGING_SSH_PRIVATE_KEY: ${{ secrets.STAGING_SSH_PRIVATE_KEY }}
STAGING_SSH_KNOWN_HOSTS: ${{ secrets.STAGING_SSH_KNOWN_HOSTS }}
NEXT_PUBLIC_API_BASE_URL: ${{ vars.NEXT_PUBLIC_API_BASE_URL }}
ADMIN_CANDIDATE_TENANT_ID: ${{ vars.ADMIN_CANDIDATE_TENANT_ID }}
INITIAL_SUPER_ADMIN_PHONE_NUMBER: ${{ vars.INITIAL_SUPER_ADMIN_PHONE_NUMBER }}
ADMIN_WEB_URL: ${{ vars.ADMIN_WEB_URL }}
BASE_URL: ${{ vars.BASE_URL }}
SMTP_ENABLED: ${{ vars.SMTP_ENABLED }}
SMTP_HOST: ${{ vars.SMTP_HOST }}
SMTP_SENDER: ${{ vars.SMTP_SENDER }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
MASTER_API_KEY: ${{ secrets.MASTER_API_KEY }}
SSO_ISSUER: ${{ vars.SSO_ISSUER }}
SSO_CLIENT_ID: ${{ vars.SSO_CLIENT_ID }}
SSO_CLIENT_SECRET: ${{ secrets.SSO_CLIENT_SECRET }}
SECRETARY_ABC_API_KEY: ${{ secrets.SECRETARY_ABC_API_KEY }}
GITEA_API_URL: ${{ vars.GITEA_API_URL }}
GITEA_API_TOKEN: ${{ secrets.EXTERNAL_ISSUE_API_TOKEN }}
GITHUB_API_TOKEN: ${{ secrets.EXTERNAL_GITHUB_API_TOKEN }}
JIRA_API_EMAIL: ${{ vars.JIRA_API_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_WEBHOOK_SECRET: ${{ secrets.JIRA_WEBHOOK_SECRET }}
JIRA_ISSUE_TYPE: ${{ vars.JIRA_ISSUE_TYPE }}
NAVER_WORKS_ENABLED: ${{ vars.NAVER_WORKS_ENABLED }}
NAVER_WORKS_API_BASE_URL: ${{ vars.NAVER_WORKS_API_BASE_URL }}
NAVER_WORKS_AUTH_URL: ${{ vars.NAVER_WORKS_AUTH_URL }}
NAVER_WORKS_BOT_ID: ${{ vars.NAVER_WORKS_BOT_ID }}
NAVER_WORKS_DEFAULT_ROOM_ID: ${{ vars.NAVER_WORKS_DEFAULT_ROOM_ID }}
NAVER_WORKS_SCOPE: ${{ vars.NAVER_WORKS_SCOPE }}
NAVER_WORKS_CLIENT_ID: ${{ secrets.NAVER_WORKS_CLIENT_ID }}
NAVER_WORKS_CLIENT_SECRET: ${{ secrets.NAVER_WORKS_CLIENT_SECRET }}
NAVER_WORKS_SERVICE_ACCOUNT: ${{ secrets.NAVER_WORKS_SERVICE_ACCOUNT }}
NAVER_WORKS_PRIVATE_KEY: ${{ secrets.NAVER_WORKS_PRIVATE_KEY }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
run: |
set -eu
STAGING_HOST="${STAGING_HOST:-172.16.10.175}"
STAGING_PORT="${STAGING_PORT:-22}"
STAGING_APP_DIR="${STAGING_APP_DIR:-/home/user/baron_qa}"
STAGING_COMPOSE_FILE="${STAGING_COMPOSE_FILE:-docker/docker-compose.prod.yml}"
export STAGING_HOST STAGING_PORT STAGING_APP_DIR STAGING_COMPOSE_FILE
required_vars="NEXT_PUBLIC_API_BASE_URL ADMIN_WEB_URL BASE_URL SSO_ISSUER SSO_CLIENT_ID ADMIN_CANDIDATE_TENANT_ID INITIAL_SUPER_ADMIN_PHONE_NUMBER NAVER_WORKS_ENABLED NAVER_WORKS_BOT_ID NAVER_WORKS_DEFAULT_ROOM_ID"
required_secrets="STAGING_USER STAGING_SSH_PRIVATE_KEY STAGING_SSH_KNOWN_HOSTS JWT_SECRET MASTER_API_KEY SSO_CLIENT_SECRET GITEA_API_TOKEN NAVER_WORKS_CLIENT_ID NAVER_WORKS_CLIENT_SECRET NAVER_WORKS_SERVICE_ACCOUNT NAVER_WORKS_PRIVATE_KEY"
test "${STAGING_HOST:-10.13.10.4}" = "10.13.10.4" || {
echo "STAGING_HOST must be 10.13.10.4" >&2
exit 1
}
test "${WEB_PORT:-8864}" = "8864" || {
echo "WEB_PORT must be 8864" >&2
exit 1
}
for name in $required_vars $required_secrets; do
SSO_CLIENT_ID="${SSO_CLIENT_ID_SECRET:-${SSO_CLIENT_ID_VAR:-}}"
export SSO_CLIENT_ID
for name in STAGING_USER STAGING_SSH_PRIVATE_KEY STAGING_SSH_KNOWN_HOSTS SSO_CLIENT_ID SSO_CLIENT_SECRET JWT_SECRET; do
if [ -z "${!name:-}" ]; then
echo "Missing Gitea variable or secret: $name" >&2
echo "Missing Gitea secret: $name" >&2
exit 1
fi
done
test "$STAGING_COMPOSE_FILE" = "docker/docker-compose.prod.yml" || {
echo "STAGING_COMPOSE_FILE must point to docker/docker-compose.prod.yml" >&2
exit 1
}
echo "Feedback demo deployment settings are present."
echo "Deployment settings are present."
- name: Deploy to staging over SSH
- name: Deploy to 10.13.10.4:8864 over SSH
env:
STAGING_HOST: ${{ vars.STAGING_HOST }}
STAGING_PORT: ${{ vars.STAGING_PORT }}
STAGING_APP_DIR: ${{ vars.STAGING_APP_DIR }}
STAGING_COMPOSE_FILE: ${{ vars.STAGING_COMPOSE_FILE }}
WEB_PORT: ${{ vars.WEB_PORT }}
SUPPORT_CONSOLE_API_BASE_URL: ${{ vars.SUPPORT_CONSOLE_API_BASE_URL }}
SSO_ISSUER: ${{ vars.SSO_ISSUER }}
SSO_AUTHORIZATION_ENDPOINT: ${{ vars.SSO_AUTHORIZATION_ENDPOINT }}
SSO_TOKEN_ENDPOINT: ${{ vars.SSO_TOKEN_ENDPOINT }}
SSO_USERINFO_ENDPOINT: ${{ vars.SSO_USERINFO_ENDPOINT }}
SSO_SCOPE: ${{ vars.SSO_SCOPE }}
SSO_CLIENT_ID_VAR: ${{ vars.SSO_CLIENT_ID }}
SSO_CLIENT_ID_SECRET: ${{ secrets.SSO_CLIENT_ID }}
SUPPORT_TENANT_ID: ${{ vars.SUPPORT_TENANT_ID }}
STAGING_USER: ${{ secrets.STAGING_USER }}
STAGING_SSH_PRIVATE_KEY: ${{ secrets.STAGING_SSH_PRIVATE_KEY }}
STAGING_SSH_KNOWN_HOSTS: ${{ secrets.STAGING_SSH_KNOWN_HOSTS }}
APP_ENV: ${{ vars.APP_ENV }}
WEB_PORT: ${{ vars.WEB_PORT }}
API_PORT: ${{ vars.API_PORT }}
SECRETARY_API_PORT: ${{ vars.SECRETARY_API_PORT }}
MYSQL_PORT: ${{ vars.MYSQL_PORT }}
MYSQL_SECRETARY_PORT: ${{ vars.MYSQL_SECRETARY_PORT }}
NEXT_PUBLIC_API_BASE_URL: ${{ vars.NEXT_PUBLIC_API_BASE_URL }}
ADMIN_CANDIDATE_TENANT_ID: ${{ vars.ADMIN_CANDIDATE_TENANT_ID }}
ADMIN_WEB_URL: ${{ vars.ADMIN_WEB_URL }}
BASE_URL: ${{ vars.BASE_URL }}
SMTP_ENABLED: ${{ vars.SMTP_ENABLED }}
SMTP_HOST: ${{ vars.SMTP_HOST }}
SMTP_PORT: ${{ vars.SMTP_PORT }}
SMTP_SENDER: ${{ vars.SMTP_SENDER }}
SMTP_TLS: ${{ vars.SMTP_TLS }}
SMTP_CIPHER_SPEC: ${{ vars.SMTP_CIPHER_SPEC }}
SMTP_OPPORTUNISTIC_TLS: ${{ vars.SMTP_OPPORTUNISTIC_TLS }}
ACCESS_TOKEN_EXPIRED_TIME: ${{ vars.ACCESS_TOKEN_EXPIRED_TIME }}
REFRESH_TOKEN_EXPIRED_TIME: ${{ vars.REFRESH_TOKEN_EXPIRED_TIME }}
AUTO_MIGRATION: ${{ vars.AUTO_MIGRATION }}
OPENSEARCH_USE: ${{ vars.OPENSEARCH_USE }}
OPENSEARCH_NODE: ${{ vars.OPENSEARCH_NODE }}
OPENSEARCH_USERNAME: ${{ vars.OPENSEARCH_USERNAME }}
OPENSEARCH_PASSWORD: ${{ vars.OPENSEARCH_PASSWORD }}
SSO_ISSUER: ${{ vars.SSO_ISSUER }}
SSO_CLIENT_ID: ${{ vars.SSO_CLIENT_ID }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
MASTER_API_KEY: ${{ secrets.MASTER_API_KEY }}
INITIAL_SUPER_ADMIN_PHONE_NUMBER: ${{ vars.INITIAL_SUPER_ADMIN_PHONE_NUMBER }}
ADMIN_CANDIDATE_EMAILS: ${{ secrets.ADMIN_CANDIDATE_EMAILS }}
SMTP_USERNAME: ${{ secrets.SMTP_USERNAME }}
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
SSO_CLIENT_SECRET: ${{ secrets.SSO_CLIENT_SECRET }}
SECRETARY_ABC_API_KEY: ${{ secrets.SECRETARY_ABC_API_KEY }}
GITEA_API_URL: ${{ vars.GITEA_API_URL }}
GITEA_API_TOKEN: ${{ secrets.EXTERNAL_ISSUE_API_TOKEN }}
GITHUB_API_TOKEN: ${{ secrets.EXTERNAL_GITHUB_API_TOKEN }}
JIRA_API_EMAIL: ${{ vars.JIRA_API_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_WEBHOOK_SECRET: ${{ secrets.JIRA_WEBHOOK_SECRET }}
JIRA_ISSUE_TYPE: ${{ vars.JIRA_ISSUE_TYPE }}
NAVER_WORKS_ENABLED: ${{ vars.NAVER_WORKS_ENABLED }}
NAVER_WORKS_API_BASE_URL: ${{ vars.NAVER_WORKS_API_BASE_URL }}
NAVER_WORKS_AUTH_URL: ${{ vars.NAVER_WORKS_AUTH_URL }}
NAVER_WORKS_BOT_ID: ${{ vars.NAVER_WORKS_BOT_ID }}
NAVER_WORKS_DEFAULT_ROOM_ID: ${{ vars.NAVER_WORKS_DEFAULT_ROOM_ID }}
NAVER_WORKS_SCOPE: ${{ vars.NAVER_WORKS_SCOPE }}
NAVER_WORKS_MAX_RETRIES: ${{ vars.NAVER_WORKS_MAX_RETRIES }}
NAVER_WORKS_MAX_MESSAGE_LENGTH: ${{ vars.NAVER_WORKS_MAX_MESSAGE_LENGTH }}
NAVER_WORKS_ACCESS_TOKEN: ${{ secrets.NAVER_WORKS_ACCESS_TOKEN }}
NAVER_WORKS_CLIENT_ID: ${{ secrets.NAVER_WORKS_CLIENT_ID }}
NAVER_WORKS_CLIENT_SECRET: ${{ secrets.NAVER_WORKS_CLIENT_SECRET }}
NAVER_WORKS_SERVICE_ACCOUNT: ${{ secrets.NAVER_WORKS_SERVICE_ACCOUNT }}
NAVER_WORKS_PRIVATE_KEY: ${{ secrets.NAVER_WORKS_PRIVATE_KEY }}
JWT_SECRET: ${{ secrets.JWT_SECRET }}
run: |
set -eu
set -o pipefail
umask 077
STAGING_HOST="${STAGING_HOST:-172.16.10.175}"
STAGING_PORT="${STAGING_PORT:-22}"
STAGING_APP_DIR="${STAGING_APP_DIR:-/home/user/baron_qa}"
STAGING_COMPOSE_FILE="${STAGING_COMPOSE_FILE:-docker/docker-compose.prod.yml}"
export STAGING_HOST STAGING_PORT STAGING_APP_DIR STAGING_COMPOSE_FILE
staging_host="${STAGING_HOST:-10.13.10.4}"
staging_port="${STAGING_PORT:-22}"
staging_app_dir="${STAGING_APP_DIR:-/home/user/egbim_qa_platform}"
web_port="${WEB_PORT:-8864}"
support_console_api_base_url="${SUPPORT_CONSOLE_API_BASE_URL:-https://feedback.hmac.kr/api/support}"
sso_issuer="${SSO_ISSUER:-https://sso.hmac.kr/oidc}"
sso_authorization_endpoint="${SSO_AUTHORIZATION_ENDPOINT:-https://sso.hmac.kr/oidc/oauth2/auth}"
sso_token_endpoint="${SSO_TOKEN_ENDPOINT:-https://sso.hmac.kr/oidc/oauth2/token}"
sso_userinfo_endpoint="${SSO_USERINFO_ENDPOINT:-https://sso.hmac.kr/oidc/userinfo}"
sso_scope="${SSO_SCOPE:-openid profile email}"
sso_client_id="${SSO_CLIENT_ID_SECRET:-${SSO_CLIENT_ID_VAR:-}}"
sso_client_secret="$SSO_CLIENT_SECRET"
jwt_secret="$JWT_SECRET"
support_tenant_id="${SUPPORT_TENANT_ID:-}"
test "$staging_host" = "10.13.10.4"
test "$web_port" = "8864"
ssh_dir="$RUNNER_TEMP/staging-ssh"
mkdir -p "$ssh_dir"
chmod 700 "$ssh_dir"
key_file="$ssh_dir/id_ed25519"
known_hosts_file="$ssh_dir/known_hosts"
printf '%s\n' "$STAGING_SSH_PRIVATE_KEY" > "$key_file"
@@ -166,31 +106,23 @@ jobs:
chmod 600 "$key_file" "$known_hosts_file"
trap 'rm -rf "$ssh_dir"' EXIT
derived_public_key="$ssh_dir/id_ed25519.pub"
if ! ssh-keygen -y -f "$key_file" > "$derived_public_key" 2>/dev/null; then
echo "STAGING_SSH_PRIVATE_KEY is invalid or passphrase-protected." >&2
exit 1
fi
key_fingerprint="$(ssh-keygen -lf "$derived_public_key" | awk '{print $2}')"
echo "SSH target: $STAGING_USER@$STAGING_HOST:$STAGING_PORT"
echo "SSH key fingerprint: $key_fingerprint"
shell_quote() {
printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\"'\\\"'/g")"
}
remote="$STAGING_USER@$STAGING_HOST"
remote_app_dir="$(shell_quote "$STAGING_APP_DIR")"
ssh-keygen -y -f "$key_file" > "$ssh_dir/id_ed25519.pub"
ssh_args=(
-i "$key_file"
-p "$STAGING_PORT"
-p "$staging_port"
-o BatchMode=yes
-o IdentitiesOnly=yes
-o StrictHostKeyChecking=yes
-o UserKnownHostsFile="$known_hosts_file"
)
remote="$STAGING_USER@$staging_host"
shell_quote() {
printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\"'\\"'/g")"
}
remote_app_dir="$(shell_quote "$staging_app_dir")"
ssh "${ssh_args[@]}" "$remote" "mkdir -p $remote_app_dir"
ssh "${ssh_args[@]}" "$remote" "rm -f $remote_app_dir/apps/secretary-api/alembic/versions/0002_role_access_workspaces.py $remote_app_dir/apps/secretary-api/alembic/versions/0009_preassigned_admin_candidates.py"
tar \
--exclude='.git' \
--exclude='node_modules' \
@@ -204,88 +136,32 @@ jobs:
--exclude='apps/docs/static' \
-czf - . | ssh "${ssh_args[@]}" "$remote" "tar -xzf - -C $remote_app_dir"
payload_file="$RUNNER_TEMP/staging-env.json"
python3 - <<'PY' > "$payload_file"
import base64
import json
import os
keys = """
APP_ENV WEB_PORT API_PORT SECRETARY_API_PORT MYSQL_PORT MYSQL_SECRETARY_PORT
NEXT_PUBLIC_API_BASE_URL ADMIN_WEB_URL BASE_URL SMTP_ENABLED SMTP_HOST SMTP_PORT SMTP_SENDER
SMTP_TLS SMTP_CIPHER_SPEC SMTP_OPPORTUNISTIC_TLS ACCESS_TOKEN_EXPIRED_TIME
REFRESH_TOKEN_EXPIRED_TIME AUTO_MIGRATION OPENSEARCH_USE OPENSEARCH_NODE
OPENSEARCH_USERNAME OPENSEARCH_PASSWORD SSO_ISSUER SSO_CLIENT_ID JWT_SECRET
MASTER_API_KEY INITIAL_SUPER_ADMIN_PHONE_NUMBER ADMIN_CANDIDATE_EMAILS ADMIN_CANDIDATE_TENANT_ID
SMTP_USERNAME SMTP_PASSWORD SSO_CLIENT_SECRET SECRETARY_ABC_API_KEY GITEA_API_URL GITEA_API_TOKEN GITHUB_API_TOKEN
JIRA_API_EMAIL JIRA_API_TOKEN JIRA_WEBHOOK_SECRET JIRA_ISSUE_TYPE
NAVER_WORKS_ENABLED NAVER_WORKS_API_BASE_URL NAVER_WORKS_AUTH_URL NAVER_WORKS_ACCESS_TOKEN
NAVER_WORKS_BOT_ID NAVER_WORKS_DEFAULT_ROOM_ID NAVER_WORKS_CLIENT_ID NAVER_WORKS_CLIENT_SECRET
NAVER_WORKS_SERVICE_ACCOUNT NAVER_WORKS_PRIVATE_KEY NAVER_WORKS_SCOPE NAVER_WORKS_MAX_RETRIES
NAVER_WORKS_MAX_MESSAGE_LENGTH
""".split()
values = {key: os.environ.get(key, "") for key in keys}
encoded = base64.b64encode(
json.dumps(values, ensure_ascii=False, separators=(",", ":")).encode()
).decode()
print(encoded)
PY
remote_compose_file="$(shell_quote "docker/docker-compose.prod.yml")"
remote_port="$(shell_quote "$web_port")"
remote_console_url="$(shell_quote "$support_console_api_base_url")"
remote_sso_issuer="$(shell_quote "$sso_issuer")"
remote_sso_authorization_endpoint="$(shell_quote "$sso_authorization_endpoint")"
remote_sso_token_endpoint="$(shell_quote "$sso_token_endpoint")"
remote_sso_userinfo_endpoint="$(shell_quote "$sso_userinfo_endpoint")"
remote_sso_scope="$(shell_quote "$sso_scope")"
remote_sso_client_id="$(shell_quote "$sso_client_id")"
remote_sso_client_secret="$(shell_quote "$sso_client_secret")"
remote_jwt_secret="$(shell_quote "$jwt_secret")"
remote_support_tenant_id="$(shell_quote "$support_tenant_id")"
remote_command="set -eu
APP_DIR=$(shell_quote "$STAGING_APP_DIR")
COMPOSE_FILE=$(shell_quote "$STAGING_COMPOSE_FILE")
test -d \"\$APP_DIR\"
cd \"\$APP_DIR\"
python3 -c 'import base64,json,os,subprocess,sys,time,urllib.request
compose=sys.argv[1]
payload=sys.stdin.buffer.read().strip()
if not payload:
raise SystemExit(\"Deployment payload is empty.\")
values=json.loads(base64.b64decode(payload))
os.environ.update(values)
subprocess.run([\"docker\",\"compose\",\"-f\",compose,\"config\",\"--quiet\"],check=True)
subprocess.run([\"docker\",\"compose\",\"-f\",compose,\"up\",\"-d\",\"--build\"],check=True)
checks=[\"http://127.0.0.1:\"+os.environ.get(\"WEB_PORT\",\"3030\")+\"/api/health\"]
for url in checks:
last_error=\"no response\"
for _ in range(60):
try:
with urllib.request.urlopen(url,timeout=3) as response:
if response.status < 500:
break
last_error=\"HTTP \"+str(response.status)
except Exception as error:
last_error=repr(error)
time.sleep(2)
else:
subprocess.run([\"docker\",\"compose\",\"-f\",compose,\"ps\"],check=False)
for service in (\"api\",\"secretary-api\",\"web\",\"mysql\",\"mysql-secretary\"):
subprocess.run([\"docker\",\"compose\",\"-f\",compose,\"logs\",\"--tail=100\",service],check=False)
raise SystemExit(\"Health check failed: \"+url+\" (\"+last_error+\")\")
secretary_check=[\"docker\",\"compose\",\"-f\",compose,\"exec\",\"-T\",\"secretary-api\",\"python3\",\"-c\",\"import urllib.request; urllib.request.urlopen(\\\"http://127.0.0.1:8010/api/health\\\", timeout=5)\"]
secretary_error=\"no response\"
for _ in range(60):
try:
subprocess.run(secretary_check,check=True)
break
except subprocess.CalledProcessError as error:
secretary_error=repr(error)
time.sleep(2)
else:
subprocess.run([\"docker\",\"compose\",\"-f\",compose,\"ps\"],check=False)
subprocess.run([\"docker\",\"compose\",\"-f\",compose,\"logs\",\"--tail=200\",\"secretary-api\"],check=False)
raise SystemExit(\"Secretary API health check failed: \"+secretary_error)
subprocess.run([\"docker\",\"compose\",\"-f\",compose,\"ps\"],check=True)
print(\"Staging deployment and health checks passed.\")' \"\$COMPOSE_FILE\""
cd $remote_app_dir
WEB_PORT=$remote_port SUPPORT_CONSOLE_API_BASE_URL=$remote_console_url SSO_ISSUER=$remote_sso_issuer SSO_AUTHORIZATION_ENDPOINT=$remote_sso_authorization_endpoint SSO_TOKEN_ENDPOINT=$remote_sso_token_endpoint SSO_USERINFO_ENDPOINT=$remote_sso_userinfo_endpoint SSO_SCOPE=$remote_sso_scope SSO_CLIENT_ID=$remote_sso_client_id SSO_CLIENT_SECRET=$remote_sso_client_secret JWT_SECRET=$remote_jwt_secret SUPPORT_TENANT_ID=$remote_support_tenant_id docker compose -f $remote_compose_file config --quiet
WEB_PORT=$remote_port SUPPORT_CONSOLE_API_BASE_URL=$remote_console_url SSO_ISSUER=$remote_sso_issuer SSO_AUTHORIZATION_ENDPOINT=$remote_sso_authorization_endpoint SSO_TOKEN_ENDPOINT=$remote_sso_token_endpoint SSO_USERINFO_ENDPOINT=$remote_sso_userinfo_endpoint SSO_SCOPE=$remote_sso_scope SSO_CLIENT_ID=$remote_sso_client_id SSO_CLIENT_SECRET=$remote_sso_client_secret JWT_SECRET=$remote_jwt_secret SUPPORT_TENANT_ID=$remote_support_tenant_id docker compose -f $remote_compose_file up -d --build
for attempt in $(seq 1 60); do
if curl -fsS http://127.0.0.1:$web_port/api/health >/dev/null; then
docker compose -f $remote_compose_file ps
echo 'Feedback demo deployment and health check passed.'
exit 0
fi
sleep 2
done
docker compose -f $remote_compose_file ps
docker compose -f $remote_compose_file logs --tail=100 web
exit 1"
cat "$payload_file" | ssh \
-i "$key_file" \
-p "$STAGING_PORT" \
-o BatchMode=yes \
-o IdentitiesOnly=yes \
-o StrictHostKeyChecking=yes \
-o UserKnownHostsFile="$known_hosts_file" \
"$remote" "$remote_command"
rm -f "$payload_file"
ssh "${ssh_args[@]}" "$remote" "$remote_command"