From f2a72888decd8af77d360bc2ff539e754522ff7b Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Thu, 30 Oct 2025 21:26:57 -0400 Subject: [PATCH 01/20] Add a .editorconfig, cribbed from zulip/zulip. --- .editorconfig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..9a0ae3c7c3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[[shell]] +binary_next_line = true +switch_case_indent = true + +[*.{yaml,yml}] +indent_size = 2 From de78ce62dbde30062bb6532805bf4e872c5e848e Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Thu, 30 Oct 2025 21:27:16 -0400 Subject: [PATCH 02/20] entrypoint: Run shfmt. --- entrypoint.sh | 127 +++++++++++++++++++++++++------------------------- 1 file changed, 63 insertions(+), 64 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index a546361ddd..1f66f21466 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -86,37 +86,37 @@ setConfigurationValue() { local TYPE="$4" if [ -z "$TYPE" ]; then case "$2" in - [Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Nn]one) - TYPE="bool" - ;; + [Tt][Rr][Uu][Ee] | [Ff][Aa][Ll][Ss][Ee] | [Nn]one) + TYPE="bool" + ;; +([0-9])) - TYPE="integer" - ;; + TYPE="integer" + ;; [\[\(]*[\]\)]) - TYPE="array" - ;; + TYPE="array" + ;; *) - TYPE="string" - ;; + TYPE="string" + ;; esac fi case "$TYPE" in emptyreturn) - if [ -z "$2" ]; then - return 0 - fi - ;; + if [ -z "$2" ]; then + return 0 + fi + ;; literal) - VALUE="$1" - ;; - bool|boolean|int|integer|array) - VALUE="$KEY = $2" - ;; - string|*) - VALUE="$KEY = '${2//\'/\'}'" - ;; + VALUE="$1" + ;; + bool | boolean | int | integer | array) + VALUE="$KEY = $2" + ;; + string | *) + VALUE="$KEY = '${2//\'/\'}'" + ;; esac - echo "$VALUE" >> "$FILE" + echo "$VALUE" >>"$FILE" echo "Setting key \"$KEY\", type \"$TYPE\" in file \"$FILE\"." } nginxConfiguration() { @@ -260,7 +260,7 @@ authenticationBackends() { echo "Activating authentication backends ..." local FIRST=true local auth_backends - IFS=, read -r -a auth_backends <<< "$ZULIP_AUTH_BACKENDS" + IFS=, read -r -a auth_backends <<<"$ZULIP_AUTH_BACKENDS" for AUTH_BACKEND in "${auth_backends[@]}"; do if [ "$FIRST" = true ]; then setConfigurationValue "AUTHENTICATION_BACKENDS" "('zproject.backends.${AUTH_BACKEND//\'/\'}',)" "$SETTINGS_PY" "array" @@ -275,7 +275,7 @@ authenticationBackends() { zulipConfiguration() { echo "Executing Zulip configuration ..." if [ -n "$ZULIP_CUSTOM_SETTINGS" ]; then - echo -e "\n$ZULIP_CUSTOM_SETTINGS" >> "$SETTINGS_PY" + echo -e "\n$ZULIP_CUSTOM_SETTINGS" >>"$SETTINGS_PY" fi local key for key in "${!SETTING_@}"; do @@ -288,35 +288,35 @@ zulipConfiguration() { continue fi # Zulip settings.py / zproject specific overrides here - if [ "$setting_key" = "AUTH_LDAP_CONNECTION_OPTIONS" ] || \ - [ "$setting_key" = "AUTH_LDAP_GLOBAL_OPTIONS" ] || \ - [ "$setting_key" = "AUTH_LDAP_USER_SEARCH" ] || \ - [ "$setting_key" = "AUTH_LDAP_GROUP_SEARCH" ] || \ - [ "$setting_key" = "AUTH_LDAP_REVERSE_EMAIL_SEARCH" ] || \ - [ "$setting_key" = "AUTH_LDAP_USER_ATTR_MAP" ] || \ - [ "$setting_key" = "AUTH_LDAP_USER_FLAGS_BY_GROUP" ] || \ - [ "$setting_key" = "AUTH_LDAP_GROUP_TYPE" ] || \ - [ "$setting_key" = "AUTH_LDAP_ADVANCED_REALM_ACCESS_CONTROL" ] || \ - [ "$setting_key" = "LDAP_SYNCHRONIZED_GROUPS_BY_REALM" ] || \ - [ "$setting_key" = "SOCIAL_AUTH_OIDC_ENABLED_IDPS" ] || \ - [ "$setting_key" = "SOCIAL_AUTH_SAML_ENABLED_IDPS" ] || \ - [ "$setting_key" = "SOCIAL_AUTH_SAML_ORG_INFO" ] || \ - [ "$setting_key" = "SOCIAL_AUTH_SYNC_ATTRS_DICT" ] || \ - { [ "$setting_key" = "LDAP_APPEND_DOMAIN" ] && [ "$setting_var" = "None" ]; } || \ - [ "$setting_key" = "SCIM_CONFIG" ] || \ - [ "$setting_key" = "SECURE_PROXY_SSL_HEADER" ] || \ - [[ "$setting_key" = "CSRF_"* ]] || \ - [ "$setting_key" = "REALM_HOSTS" ] || \ - [ "$setting_key" = "ALLOWED_HOSTS" ]; then + if [ "$setting_key" = "AUTH_LDAP_CONNECTION_OPTIONS" ] \ + || [ "$setting_key" = "AUTH_LDAP_GLOBAL_OPTIONS" ] \ + || [ "$setting_key" = "AUTH_LDAP_USER_SEARCH" ] \ + || [ "$setting_key" = "AUTH_LDAP_GROUP_SEARCH" ] \ + || [ "$setting_key" = "AUTH_LDAP_REVERSE_EMAIL_SEARCH" ] \ + || [ "$setting_key" = "AUTH_LDAP_USER_ATTR_MAP" ] \ + || [ "$setting_key" = "AUTH_LDAP_USER_FLAGS_BY_GROUP" ] \ + || [ "$setting_key" = "AUTH_LDAP_GROUP_TYPE" ] \ + || [ "$setting_key" = "AUTH_LDAP_ADVANCED_REALM_ACCESS_CONTROL" ] \ + || [ "$setting_key" = "LDAP_SYNCHRONIZED_GROUPS_BY_REALM" ] \ + || [ "$setting_key" = "SOCIAL_AUTH_OIDC_ENABLED_IDPS" ] \ + || [ "$setting_key" = "SOCIAL_AUTH_SAML_ENABLED_IDPS" ] \ + || [ "$setting_key" = "SOCIAL_AUTH_SAML_ORG_INFO" ] \ + || [ "$setting_key" = "SOCIAL_AUTH_SYNC_ATTRS_DICT" ] \ + || { [ "$setting_key" = "LDAP_APPEND_DOMAIN" ] && [ "$setting_var" = "None" ]; } \ + || [ "$setting_key" = "SCIM_CONFIG" ] \ + || [ "$setting_key" = "SECURE_PROXY_SSL_HEADER" ] \ + || [[ "$setting_key" = "CSRF_"* ]] \ + || [ "$setting_key" = "REALM_HOSTS" ] \ + || [ "$setting_key" = "ALLOWED_HOSTS" ]; then type="array" fi - if [ "$SPECIAL_SETTING_DETECTION_MODE" = "True" ] || [ "$SPECIAL_SETTING_DETECTION_MODE" = "true" ] || \ - [ "$type" = "string" ]; then + if [ "$SPECIAL_SETTING_DETECTION_MODE" = "True" ] || [ "$SPECIAL_SETTING_DETECTION_MODE" = "true" ] \ + || [ "$type" = "string" ]; then type="" fi - if [ "$setting_key" = "EMAIL_HOST_USER" ] || \ - [ "$setting_key" = "EMAIL_HOST_PASSWORD" ] || \ - [ "$setting_key" = "EXTERNAL_HOST" ]; then + if [ "$setting_key" = "EMAIL_HOST_USER" ] \ + || [ "$setting_key" = "EMAIL_HOST_PASSWORD" ] \ + || [ "$setting_key" = "EXTERNAL_HOST" ]; then type="string" fi setConfigurationValue "$setting_key" "$setting_var" "$SETTINGS_PY" "$type" @@ -333,7 +333,7 @@ autoBackupConfiguration() { echo "Auto backup is disabled. Continuing." return 0 fi - printf 'MAILTO=""\n%s cd /;/sbin/entrypoint.sh app:backup\n' "$AUTO_BACKUP_INTERVAL" > /etc/cron.d/autobackup + printf 'MAILTO=""\n%s cd /;/sbin/entrypoint.sh app:backup\n' "$AUTO_BACKUP_INTERVAL" >/etc/cron.d/autobackup echo "Auto backup enabled." } initialConfiguration() { @@ -357,8 +357,7 @@ initialConfiguration() { waitingForDatabase() { local TIMEOUT=60 echo "Waiting for database server to allow connections ..." - while ! PGPASSWORD="${SECRETS_postgres_password?}" /usr/bin/pg_isready -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" -t 1 >/dev/null 2>&1 - do + while ! PGPASSWORD="${SECRETS_postgres_password?}" /usr/bin/pg_isready -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" -t 1 >/dev/null 2>&1; do if ! ((TIMEOUT--)); then echo "Could not connect to database server. Exiting." exit 1 @@ -435,7 +434,7 @@ function runCertbotAsNeeded() { echo "Waiting for nginx to come online before generating certbot certificate ..." while ! curl -sk "$SETTING_EXTERNAL_HOST" >/dev/null 2>&1; do - sleep 1; + sleep 1 done echo "Generating LetsEncrypt/certbot certificate ..." @@ -507,7 +506,7 @@ appBackup() { BACKUP_FOLDER="/tmp/backup-$TIMESTAMP)" mkdir -p "$BACKUP_FOLDER" waitingForDatabase - pg_dump -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" "$DB_NAME" > "$BACKUP_FOLDER/database-postgres.sql" + pg_dump -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" "$DB_NAME" >"$BACKUP_FOLDER/database-postgres.sql" tar -zcvf "$DATA_DIR/backups/backup-$TIMESTAMP.tar.gz" "$BACKUP_FOLDER/" rm -r "${BACKUP_FOLDER:?}/" echo "Backup process succeeded." @@ -552,7 +551,7 @@ appRestore() { echo "!! WARNING !! Starting restore process ... !! WARNING !!" waitingForDatabase tar -zxvf "$DATA_DIR/backups/$BACKUP_FILE" -C /tmp - psql -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" "$DB_NAME" < "/tmp/$(basename "$BACKUP_FILE" | cut -d. -f1)/database-postgres.sql" + psql -h "$DB_HOST" -p "$DB_HOST_PORT" -U "$DB_USER" "$DB_NAME" <"/tmp/$(basename "$BACKUP_FILE" | cut -d. -f1)/database-postgres.sql" rm -r "/tmp/$(basename "$BACKUP_FILE" | cut -d. -f1)/" echo "Restore process succeeded. Exiting." exit 0 @@ -583,30 +582,30 @@ appVersion() { case "$1" in app:run) appRun - ;; + ;; app:init) appInit - ;; + ;; app:managepy) shift 1 appManagePy "$@" - ;; + ;; app:backup) appBackup - ;; + ;; app:restore) appRestore - ;; + ;; app:certs) appCerts - ;; + ;; app:help) appHelp - ;; + ;; app:version) appVersion - ;; + ;; *) exec "$@" || appHelp - ;; + ;; esac From d467b7e487960c7dc3ff132883647152536b9ab0 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Thu, 30 Oct 2025 21:52:37 -0400 Subject: [PATCH 03/20] upgrade-postgresql: Run shfmt. --- upgrade-postgresql | 68 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/upgrade-postgresql b/upgrade-postgresql index 1b50f92ca5..0e5cef2f92 100755 --- a/upgrade-postgresql +++ b/upgrade-postgresql @@ -5,36 +5,36 @@ new_version=14 # Require the `yq` tool if ! command -v yq >/dev/null; then - echo "You must install the 'yq' tool to use this script." - exit 1 + echo "You must install the 'yq' tool to use this script." + exit 1 fi # Require docker compose 2.1.1 or higher, for `docker compose up --wait` docker_compose_version=$(docker compose --version --short) if [ "${docker_compose_version}" = "$(echo -e "2.1.0\n${docker_compose_version}" | sort -V | head -n1)" ]; then - echo "Your docker compose is too old (${docker_compose_version}); upgrade to at least 2.1.1." - exit 1 + echo "Your docker compose is too old (${docker_compose_version}); upgrade to at least 2.1.1." + exit 1 fi image=$(yq ".services.database.image" docker-compose.yml) if [[ $image =~ ^zulip/zulip-postgresql:([0-9]+)$ ]]; then - old_version="${BASH_REMATCH[1]}" + old_version="${BASH_REMATCH[1]}" else - echo "Unexpected PostgreSQL image: $image" - exit 1 + echo "Unexpected PostgreSQL image: $image" + exit 1 fi volume_mount=$(yq ".services.database.volumes.0" docker-compose.yml) if [[ "$volume_mount" =~ ^([^:]+):/var/lib/postgresql/data:rw$ ]]; then - old_mountpoint="${BASH_REMATCH[1]}" + old_mountpoint="${BASH_REMATCH[1]}" else - echo "Unexpected volume mount: $volume_mount" - exit 1 + echo "Unexpected volume mount: $volume_mount" + exit 1 fi if [ "$new_version" -eq "$old_version" ]; then - echo "PostgreSQL image is already version $new_version!" - exit 1 + echo "PostgreSQL image is already version $new_version!" + exit 1 fi # Create a new volume for the data; scope it with the current @@ -48,26 +48,26 @@ trap 'docker volume --force "$full_new_volume"' EXIT # Start a new PostgreSQL container of the right version to read in the # dumped database and write a new data dir on the new volume temp_container=$( - docker run -d \ - -e POSTGRES_DB=zulip \ - -e POSTGRES_USER=zulip \ - -e POSTGRES_PASSWORD=zulip \ - -v "$full_new_volume:/var/lib/postgresql/data:rw" \ - --health-cmd 'psql -U zulip -c "select 1"' \ - --health-interval 10s \ - "zulip/zulip-postgresql:$new_version" + docker run -d \ + -e POSTGRES_DB=zulip \ + -e POSTGRES_USER=zulip \ + -e POSTGRES_PASSWORD=zulip \ + -v "$full_new_volume:/var/lib/postgresql/data:rw" \ + --health-cmd 'psql -U zulip -c "select 1"' \ + --health-interval 10s \ + "zulip/zulip-postgresql:$new_version" ) trap 'docker rm --force "$temp_container"; docker volume rm --force "$full_new_volume"' EXIT # Wait for the new PostgreSQL container to become available tries=0 while [ "$(docker inspect --format='{{json .State.Health.Status}}' "$temp_container")" != '"healthy"' ]; do - tries=$((tries + 1)) - if [ "$tries" -gt 5 ]; then - echo "PostgreSQL $new_version container failed to start!" - exit 1 - fi - sleep 10 + tries=$((tries + 1)) + if [ "$tries" -gt 5 ]; then + echo "PostgreSQL $new_version container failed to start!" + exit 1 + fi + sleep 10 done # Ensure database is running @@ -76,19 +76,19 @@ docker compose up --wait database # Stop the zulip processes which talk to the database zulip_is_running=$(docker compose ps --filter status=running --services | grep zulip || true) if [ -n "$zulip_is_running" ]; then - docker compose stop zulip + docker compose stop zulip fi # Transfer the data to the new container -docker compose exec database pg_dumpall -U zulip | - docker exec -i "$temp_container" psql -U zulip +docker compose exec database pg_dumpall -U zulip \ + | docker exec -i "$temp_container" psql -U zulip if [ "$old_version" -eq "10" ]; then - # Upgrade MD5 password to SCRAM-SHA-256. We escape all 's by doubling them. - database_password=$(yq .services.database.environment.POSTGRES_PASSWORD docker-compose.yml | - perl -pe "s/'/''/g") - echo "ALTER USER zulip WITH PASSWORD '$database_password';" | - docker exec -i "$temp_container" psql -U zulip + # Upgrade MD5 password to SCRAM-SHA-256. We escape all 's by doubling them. + database_password=$(yq .services.database.environment.POSTGRES_PASSWORD docker-compose.yml \ + | perl -pe "s/'/''/g") + echo "ALTER USER zulip WITH PASSWORD '$database_password';" \ + | docker exec -i "$temp_container" psql -U zulip fi # Stop the running database From 5c4773b53631c430aa3fb4240be075f4ce41beda Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Thu, 30 Oct 2025 21:27:39 -0400 Subject: [PATCH 04/20] github: Add shellcheck + shellfmt action. --- .github/workflows/shell-test.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/shell-test.yaml diff --git a/.github/workflows/shell-test.yaml b/.github/workflows/shell-test.yaml new file mode 100644 index 0000000000..692ee19aec --- /dev/null +++ b/.github/workflows/shell-test.yaml @@ -0,0 +1,14 @@ +--- +name: Shell check and lint + +on: + pull_request: + branches: + - main + +jobs: + shellcheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: luizm/action-sh-checker@master From 97ba4df7f0cc8b8cad85214008c13ad0f73e51de Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 20:40:18 -0500 Subject: [PATCH 05/20] typos: Fix typos found by typos. --- UPGRADING.md | 2 +- entrypoint.sh | 6 +++--- kubernetes/manual/zulip-rc.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/UPGRADING.md b/UPGRADING.md index f5d340e8d7..e0c246f9c7 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -219,7 +219,7 @@ image (because we've significantly upgraded the major postgres version). These instructions assume that you have not changed the default PostgreSQL data path (`/opt/docker/zulip/postgresql/data`) in your `docker-compose.yml`. If you -have changed it, please replace all occurences of +have changed it, please replace all occurrences of `/opt/docker/zulip/postgresql/data` with your path. 1. Make a backup of your Zulip PostgreSQL data directory. diff --git a/entrypoint.sh b/entrypoint.sh index 1f66f21466..617dc6dd91 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -30,7 +30,7 @@ DISABLE_HTTPS="${DISABLE_HTTPS:-false}" NGINX_WORKERS="${NGINX_WORKERS:-2}" NGINX_PROXY_BUFFERING="${NGINX_PROXY_BUFFERING:-off}" NGINX_MAX_UPLOAD_SIZE="${NGINX_MAX_UPLOAD_SIZE:-80m}" -# Zulip certifcate parameters +# Zulip certificate parameters SSL_CERTIFICATE_GENERATION="${SSL_CERTIFICATE_GENERATION:self-signed}" # Zulip related settings ZULIP_AUTH_BACKENDS="${ZULIP_AUTH_BACKENDS:-EmailAuthBackend}" @@ -382,7 +382,7 @@ zulipFirstStartInit() { fi set -e touch "$DATA_DIR/.initiated" - echo "Zulip first start init sucessful." + echo "Zulip first start init successful." } zulipMigration() { echo "Running new database migrations..." @@ -568,7 +568,7 @@ appHelp() { echo "> app:restore - Restore backups of Zulip instances" echo "> app:certs - Create self-signed certificates" echo "> app:run - Run the Zulip server" - echo "> app:init - Run inital setup of Zulip server" + echo "> app:init - Run initial setup of Zulip server" echo "> [COMMAND] - Run given command with arguments in shell" } appVersion() { diff --git a/kubernetes/manual/zulip-rc.yml b/kubernetes/manual/zulip-rc.yml index 52ef5b0b6d..efbdb95163 100644 --- a/kubernetes/manual/zulip-rc.yml +++ b/kubernetes/manual/zulip-rc.yml @@ -121,7 +121,7 @@ spec: - name: ZULIP_USER_PASS value: "123456789" - name: SECRETS_secret_key - value: "REPLCAE_WITH_SECURE_SECRET_KEY" + value: "REPLACE_WITH_SECURE_SECRET_KEY" ## These should match the passwords configured above - name: SECRETS_postgres_password value: "REPLACE_WITH_SECURE_POSTGRES_PASSWORD" From 9b897e07d12e633a73a4eae9581c2ed7b8a36f0d Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 20:42:13 -0500 Subject: [PATCH 06/20] github: Spellcheck with typos. --- .github/workflows/spelling.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/spelling.yaml diff --git a/.github/workflows/spelling.yaml b/.github/workflows/spelling.yaml new file mode 100644 index 0000000000..30d1553846 --- /dev/null +++ b/.github/workflows/spelling.yaml @@ -0,0 +1,16 @@ +--- +name: Spelling + +on: + pull_request: + branches: + - main + +jobs: + spelling: + name: Spellcheck with Typos + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Spell Check Repo + uses: crate-ci/typos@v1.39.0 From 63819f50b68a8024dffc7151a10fd9b1ac41b894 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Thu, 6 Nov 2025 11:42:09 -0500 Subject: [PATCH 07/20] entrypoint: Better error handling on MANUAL_CONFIGURATION errors. Fixes: #513, #517. --- entrypoint.sh | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/entrypoint.sh b/entrypoint.sh index 617dc6dd91..c4833617e4 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -349,6 +349,33 @@ initialConfiguration() { secretsConfiguration authenticationBackends zulipConfiguration + else + # Check that the configuration will work + root_path="/etc/zulip" + if [ "$LINK_SETTINGS_TO_DATA" = "True" ] || [ "$LINK_SETTINGS_TO_DATA" = "true" ]; then + root_path="/data/settings/etc-zulip" + fi + failure=0 + for conf_file in zulip.conf zulip-secrets.conf settings.py; do + if [ ! -f "/etc/zulip/$conf_file" ]; then + echo "ERROR: $root_path/$conf_file does not exist!" + failure=1 + elif ! sudo -u zulip test -r "/etc/zulip/$conf_file"; then + echo "ERROR: $root_path/$conf_file is not readable by the zulip user (UID $(id -u zulip))" + failure=1 + elif [ ! -s "/etc/zulip/$conf_file" ]; then + echo "ERROR: $root_path/$conf_file is empty" + failure=1 + fi + done + if [ "$failure" = "1" ]; then + ls -l /etc/zulip/ + exit 1 + fi + if ! su zulip -c "/home/zulip/deployments/current/manage.py checkconfig"; then + echo "Error in the Zulip configuration. Exiting." + exit 1 + fi fi autoBackupConfiguration echo "=== End Initial Configuration Phase ===" From 064dfdff795931338de3df6659a7d4d0f3257f81 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Thu, 30 Oct 2025 15:58:12 -0400 Subject: [PATCH 08/20] Dockerfile: Pass `hadolint`. --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 87ff46e891..8b6bdb5fde 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,7 @@ ENV LANG="C.UTF-8" ARG UBUNTU_MIRROR +# hadolint ignore=DL3005,DL3008,DL3009 RUN { [ ! "$UBUNTU_MIRROR" ] || sed -i "s|http://\(\w*\.\)*archive\.ubuntu\.com/ubuntu/\? |$UBUNTU_MIRROR |" /etc/apt/sources.list; } && \ apt-get -q update && \ apt-get -q dist-upgrade -y && \ @@ -29,8 +30,7 @@ WORKDIR /home/zulip ARG ZULIP_GIT_URL=https://github.com/zulip/zulip.git ARG ZULIP_GIT_REF=11.4 -RUN git clone "$ZULIP_GIT_URL" -b "$ZULIP_GIT_REF" && \ - cd zulip +RUN git clone "$ZULIP_GIT_URL" -b "$ZULIP_GIT_REF" WORKDIR /home/zulip/zulip @@ -53,12 +53,12 @@ COPY custom_zulip_files/ /root/custom_zulip ARG CUSTOM_CA_CERTIFICATES +WORKDIR /root RUN \ # Make sure Nginx is started by Supervisor. dpkg-divert --add --rename /etc/init.d/nginx && \ ln -s /bin/true /etc/init.d/nginx && \ mkdir -p "$DATA_DIR" && \ - cd /root && \ tar -xf zulip-server-docker.tar.gz && \ rm -f zulip-server-docker.tar.gz && \ mv zulip-server-docker zulip && \ From a55ac509ed4de48bf5294ba65feecad212be5e55 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Thu, 30 Oct 2025 15:58:49 -0400 Subject: [PATCH 09/20] github: Add hadolint action. --- .github/workflows/dockerfile.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/dockerfile.yaml diff --git a/.github/workflows/dockerfile.yaml b/.github/workflows/dockerfile.yaml new file mode 100644 index 0000000000..4676c3b5e5 --- /dev/null +++ b/.github/workflows/dockerfile.yaml @@ -0,0 +1,16 @@ +--- +name: Dockerfile lint + +on: + pull_request: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - uses: hadolint/hadolint-action@v3.1.0 From f460e46d54a39c603b07bddb63f9f8c6a05ad727 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 20:46:02 -0500 Subject: [PATCH 10/20] github: Build and push a `main` image. Partially addresses #106. --- .github/workflows/dockerfile-build.yaml | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/dockerfile-build.yaml diff --git a/.github/workflows/dockerfile-build.yaml b/.github/workflows/dockerfile-build.yaml new file mode 100644 index 0000000000..0a6f06a514 --- /dev/null +++ b/.github/workflows/dockerfile-build.yaml @@ -0,0 +1,36 @@ +--- +name: Build and push `main` image + +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ghcr.io/${{ github.repository }}:main + cache-from: type=gha + cache-to: type=gha,mode=max From c9b6c4bd8cfe9c75756dcf517bed1359c5ae9bcd Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 12:25:59 -0500 Subject: [PATCH 11/20] github: Build and push an image for each PR. --- .github/workflows/dockerfile-cleanup.yaml | 22 ++++++++++++++++ .github/workflows/dockerfile.yaml | 31 ++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/dockerfile-cleanup.yaml diff --git a/.github/workflows/dockerfile-cleanup.yaml b/.github/workflows/dockerfile-cleanup.yaml new file mode 100644 index 0000000000..d040d980d7 --- /dev/null +++ b/.github/workflows/dockerfile-cleanup.yaml @@ -0,0 +1,22 @@ +--- +name: Cleanup closed PR images + +on: + pull_request: + branches: + - main + types: [closed] + +jobs: + cleanup: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Delete PR image + uses: actions/delete-package-versions@v5 + with: + package-name: "zulip/docker-zulip" + package-type: "container" + token: ${{ secrets.GITHUB_TOKEN }} + tag-name: pr-${{ github.event.pull_request.number }} diff --git a/.github/workflows/dockerfile.yaml b/.github/workflows/dockerfile.yaml index 4676c3b5e5..b5f2951321 100644 --- a/.github/workflows/dockerfile.yaml +++ b/.github/workflows/dockerfile.yaml @@ -1,5 +1,5 @@ --- -name: Dockerfile lint +name: Dockerfile lint and build on: pull_request: @@ -14,3 +14,32 @@ jobs: uses: actions/checkout@v5 - uses: hadolint/hadolint-action@v3.1.0 + + build: + runs-on: ubuntu-latest + needs: lint + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ghcr.io/${{ github.repository }}:pr-${{ github.event.pull_request.number }} + cache-from: type=gha + cache-to: type=gha,mode=max From 54bdc5d0e5f2734533979b48471ce553d70ab42d Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Thu, 30 Oct 2025 16:24:30 -0400 Subject: [PATCH 12/20] helm: Pass `ct lint`. --- kubernetes/chart/zulip/Chart.yaml | 5 ++++- kubernetes/chart/zulip/README.md | 2 +- lintconf.yaml | 5 +++++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 lintconf.yaml diff --git a/kubernetes/chart/zulip/Chart.yaml b/kubernetes/chart/zulip/Chart.yaml index 840e2945d9..eacc22cd80 100644 --- a/kubernetes/chart/zulip/Chart.yaml +++ b/kubernetes/chart/zulip/Chart.yaml @@ -3,11 +3,14 @@ description: Zulip is an open source threaded team chat that helps teams stay pr name: zulip type: application icon: https://raw.githubusercontent.com/zulip/zulip/main/static/images/logo/zulip-icon-square.svg +maintainers: + - name: zulip + url: https://chat.zulip.org/ ## This is the chart version. This version number should be ## incremented each time you make changes to the chart and its ## templates, including the app version. Versions are expected to ## follow Semantic Versioning (https://semver.org/) -version: 0.11.4 +version: 0.11.41 ## This is the version number of the application being deployed. This ## version number should be incremented each time you make changes to diff --git a/kubernetes/chart/zulip/README.md b/kubernetes/chart/zulip/README.md index 811b582876..526c9dfad6 100644 --- a/kubernetes/chart/zulip/README.md +++ b/kubernetes/chart/zulip/README.md @@ -1,6 +1,6 @@ # Zulip -![Version: 0.11.4](https://img.shields.io/badge/Version-0.11.4-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 11.4-0](https://img.shields.io/badge/AppVersion-11.4--0-informational?style=flat-square) +![Version: 0.11.41](https://img.shields.io/badge/Version-0.11.41-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 11.4-0](https://img.shields.io/badge/AppVersion-11.4--0-informational?style=flat-square) [Zulip](https://zulip.com/) is an open source threaded team chat that helps teams stay productive and focused. diff --git a/lintconf.yaml b/lintconf.yaml new file mode 100644 index 0000000000..125bb08cb6 --- /dev/null +++ b/lintconf.yaml @@ -0,0 +1,5 @@ +--- +# This is consumed by `yamllint`, which is run by `ct lint` +rules: + comments: + min-spaces-from-content: 1 # Due to prettier: https://github.com/prettier/prettier/pull/10926 From 6e3ed0ff2dece2c497d0297a1ee886cbed900130 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Wed, 5 Nov 2025 23:30:34 -0500 Subject: [PATCH 13/20] helm: Fix containerSecurityContext to run PostgreSQL as non-root. Fixes: #470. --- kubernetes/chart/zulip/README.md | 4 +++- kubernetes/chart/zulip/values.yaml | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/kubernetes/chart/zulip/README.md b/kubernetes/chart/zulip/README.md index 526c9dfad6..884bc89a5a 100644 --- a/kubernetes/chart/zulip/README.md +++ b/kubernetes/chart/zulip/README.md @@ -96,7 +96,9 @@ Now you're ready to follow [the installation instructions above](#installation). | postgresql.auth.username | string | `"zulip"` | | | postgresql.image.repository | string | `"zulip/zulip-postgresql"` | | | postgresql.image.tag | int | `14` | | -| postgresql.primary.containerSecurityContext.runAsUser | int | `0` | | +| postgresql.primary.containerSecurityContext.readOnlyRootFilesystem | bool | `false` | | +| postgresql.primary.containerSecurityContext.runAsGroup | int | `70` | | +| postgresql.primary.containerSecurityContext.runAsUser | int | `70` | | | rabbitmq.auth.username | string | `"zulip"` | | | rabbitmq.persistence.enabled | bool | `false` | | | redis.architecture | string | `"standalone"` | | diff --git a/kubernetes/chart/zulip/values.yaml b/kubernetes/chart/zulip/values.yaml index b11539b16d..d9aca8a80b 100644 --- a/kubernetes/chart/zulip/values.yaml +++ b/kubernetes/chart/zulip/values.yaml @@ -200,7 +200,12 @@ sidecars: [] postgresql: primary: containerSecurityContext: - runAsUser: 0 + # 70 is the standard uid/gid of the "postgres" user in Alpine, which is + # used as the base for zulip/zulip-postgresql + # https://github.com/docker-library/postgres/blob/23987751b63ce745bd27b1119ab29dc4a1ffd728/Dockerfile-alpine.template#L7 + runAsUser: 70 + runAsGroup: 70 + readOnlyRootFilesystem: false ## We need to override the Postgresql image to get all the plugins Zulip needs image: repository: zulip/zulip-postgresql From 0fe652a8e302c95cad05cf6381017248176a5b1f Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 20:49:36 -0500 Subject: [PATCH 14/20] helm: Switch to bitnamilegacy images. These images are not maintained; using them is not a viable long-term solution. This causes the Helm chart to at least be installable, while a long-term fix (most likely by moving off the of Bitnami charts and images) is worked on. Fixes #521. See also #506. --- kubernetes/chart/zulip/Chart.lock | 12 ++++++------ kubernetes/chart/zulip/Chart.yaml | 8 ++++---- kubernetes/chart/zulip/README.md | 16 ++++++++++++---- kubernetes/chart/zulip/values.yaml | 17 +++++++++++++++++ 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/kubernetes/chart/zulip/Chart.lock b/kubernetes/chart/zulip/Chart.lock index a227174d48..8ac8b5cf99 100644 --- a/kubernetes/chart/zulip/Chart.lock +++ b/kubernetes/chart/zulip/Chart.lock @@ -1,15 +1,15 @@ dependencies: - name: memcached repository: oci://registry-1.docker.io/bitnamicharts - version: 7.4.16 + version: 8.1.5 - name: rabbitmq repository: oci://registry-1.docker.io/bitnamicharts - version: 14.7.0 + version: 16.0.14 - name: redis repository: oci://registry-1.docker.io/bitnamicharts - version: 20.1.4 + version: 23.2.12 - name: postgresql repository: oci://registry-1.docker.io/bitnamicharts - version: 15.5.32 -digest: sha256:511a69dac54b26810b3b269751c8115d5b8ed7a9ecfe5cc4a656e7feb5dbd1ff -generated: "2024-09-23T17:27:41.004706+02:00" + version: 18.1.8 +digest: sha256:8e1f3e3c40783b573c8551e100b319d88a8dbd630d5f41d228be0621c5ef1bb5 +generated: "2025-11-10T09:49:32.026785-05:00" diff --git a/kubernetes/chart/zulip/Chart.yaml b/kubernetes/chart/zulip/Chart.yaml index eacc22cd80..962939bc5a 100644 --- a/kubernetes/chart/zulip/Chart.yaml +++ b/kubernetes/chart/zulip/Chart.yaml @@ -23,23 +23,23 @@ dependencies: repository: oci://registry-1.docker.io/bitnamicharts tags: - memcached - version: 7.4.16 + version: 8.1.5 - name: rabbitmq repository: oci://registry-1.docker.io/bitnamicharts tags: - rabbitmq - version: 14.7.0 + version: 16.0.14 - name: redis repository: oci://registry-1.docker.io/bitnamicharts tags: - redis - version: 20.1.4 + version: 23.2.12 - name: postgresql repository: oci://registry-1.docker.io/bitnamicharts tags: - postgresql ## Note: values.yaml overwrites posgresql image to zulip/zulip-postgresql:14 - version: 15.5.32 + version: 18.1.8 sources: - https://github.com/zulip/zulip diff --git a/kubernetes/chart/zulip/README.md b/kubernetes/chart/zulip/README.md index 884bc89a5a..a842d3f2cc 100644 --- a/kubernetes/chart/zulip/README.md +++ b/kubernetes/chart/zulip/README.md @@ -70,6 +70,7 @@ Now you're ready to follow [the installation instructions above](#installation). |-----|------|---------|-------------| | affinity | object | `{}` | | | fullnameOverride | string | `""` | | +| global.security.allowInsecureImages | bool | `true` | | | image.pullPolicy | string | `"IfNotPresent"` | | | image.repository | string | `"zulip/docker-zulip"` | | | image.tag | string | `"11.4-0"` | | @@ -85,6 +86,8 @@ Now you're ready to follow [the installation instructions above](#installation). | livenessProbe.periodSeconds | int | `10` | | | livenessProbe.successThreshold | int | `1` | | | livenessProbe.timeoutSeconds | int | `5` | | +| memcached.image.repository | string | `"bitnamilegacy/memcached"` | | +| memcached.image.tag | string | `"latest"` | | | memcached.memcachedUsername | string | `"zulip@localhost"` | | | nameOverride | string | `""` | | | nodeSelector | object | `{}` | | @@ -100,8 +103,12 @@ Now you're ready to follow [the installation instructions above](#installation). | postgresql.primary.containerSecurityContext.runAsGroup | int | `70` | | | postgresql.primary.containerSecurityContext.runAsUser | int | `70` | | | rabbitmq.auth.username | string | `"zulip"` | | +| rabbitmq.image.repository | string | `"bitnamilegacy/rabbitmq"` | | +| rabbitmq.image.tag | string | `"4.1.3"` | | | rabbitmq.persistence.enabled | bool | `false` | | | redis.architecture | string | `"standalone"` | | +| redis.image.repository | string | `"bitnamilegacy/redis"` | | +| redis.image.tag | string | `"latest"` | | | redis.master.persistence.enabled | bool | `false` | | | resources | object | `{}` | | | securityContext | object | `{}` | | @@ -130,6 +137,7 @@ Now you're ready to follow [the installation instructions above](#installation). | zulip.environment.SETTING_EXTERNAL_HOST | string | `"zulip.example.com"` | | | zulip.environment.SETTING_ZULIP_ADMINISTRATOR | string | `"admin@example.com"` | | | zulip.environment.SSL_CERTIFICATE_GENERATION | string | `"self-signed"` | | +| zulip.environment.TRUST_GATEWAY_IP | bool | `true` | | | zulip.environment.ZULIP_AUTH_BACKENDS | string | `"EmailAuthBackend"` | | | zulip.persistence.accessMode | string | `"ReadWriteOnce"` | | | zulip.persistence.enabled | bool | `true` | | @@ -176,7 +184,7 @@ image, because it contains the Postgresql plugins that are needed to run Zulip. | Repository | Name | Version | |------------|------|---------| -| oci://registry-1.docker.io/bitnamicharts | memcached | 7.4.16 | -| oci://registry-1.docker.io/bitnamicharts | postgresql | 15.5.32 | -| oci://registry-1.docker.io/bitnamicharts | rabbitmq | 14.7.0 | -| oci://registry-1.docker.io/bitnamicharts | redis | 20.1.4 | +| oci://registry-1.docker.io/bitnamicharts | memcached | 8.1.5 | +| oci://registry-1.docker.io/bitnamicharts | postgresql | 18.1.8 | +| oci://registry-1.docker.io/bitnamicharts | rabbitmq | 16.0.14 | +| oci://registry-1.docker.io/bitnamicharts | redis | 23.2.12 | diff --git a/kubernetes/chart/zulip/values.yaml b/kubernetes/chart/zulip/values.yaml index d9aca8a80b..40c0610b1f 100644 --- a/kubernetes/chart/zulip/values.yaml +++ b/kubernetes/chart/zulip/values.yaml @@ -196,6 +196,11 @@ postSetup: # mountPath: /data sidecars: [] +## Bitnami requires this to override PostgreSQL image +global: + security: + allowInsecureImages: true + ## PostgreSQL settings, see [Requirements](#Requirements). postgresql: primary: @@ -216,6 +221,10 @@ postgresql: ## Rabbitmq settings, see [Requirements](#Requirements). rabbitmq: + image: + # TODO: This image is no longer updated, and will need to be updated to one which is. + repository: bitnamilegacy/rabbitmq + tag: 4.1.3 auth: username: zulip ## Set this to true if you need the rabbitmq to be persistent @@ -224,10 +233,18 @@ rabbitmq: ## Memcached settings, see [Requirements](#Requirements). memcached: + image: + # TODO: This image is no longer updated, and will need to be updated to one which is. + repository: bitnamilegacy/memcached + tag: latest memcachedUsername: "zulip@localhost" ## Redis settings, see [Requirements](#Requirements). redis: + image: + # TODO: This image is no longer updated, and will need to be updated to one which is. + repository: bitnamilegacy/redis + tag: latest architecture: standalone master: persistence: From 39a714ddbf58fd46905964356c5699f4dafa4b5e Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 20:50:26 -0500 Subject: [PATCH 15/20] helm: Fix DB_USER to be zulip. Zulip should connect as the unprivileged zulip user, not postgres. --- kubernetes/chart/zulip/templates/_helpers.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kubernetes/chart/zulip/templates/_helpers.tpl b/kubernetes/chart/zulip/templates/_helpers.tpl index a6fbde30a8..9af393cfcd 100644 --- a/kubernetes/chart/zulip/templates/_helpers.tpl +++ b/kubernetes/chart/zulip/templates/_helpers.tpl @@ -71,7 +71,7 @@ include all env variables for Zulip pods - name: DB_HOST_PORT value: "{{ template "postgresql.v1.service.port" .Subcharts.postgresql }}" - name: DB_USER - value: "postgres" + value: "zulip" - name: SETTING_MEMCACHED_LOCATION value: "{{ template "common.names.fullname" .Subcharts.memcached }}:11211" - name: SETTING_RABBITMQ_HOST From e2cd5b7eff88cdc6b7957bd4710b08795a9b7b91 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 20:51:18 -0500 Subject: [PATCH 16/20] helm: Fix comments for the right template names. --- kubernetes/chart/zulip/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kubernetes/chart/zulip/values.yaml b/kubernetes/chart/zulip/values.yaml index 40c0610b1f..7c556d369e 100644 --- a/kubernetes/chart/zulip/values.yaml +++ b/kubernetes/chart/zulip/values.yaml @@ -22,10 +22,10 @@ image: ## Global Docker registry secret names as an array. imagePullSecrets: [] -## Partially override common.names.fullname template (will maintain the release name). +## Partially override zulip.fullname template (will maintain the release name). nameOverride: "" -## Fully override common.names.fullname template. +## Fully override zulip.fullname template. fullnameOverride: "" serviceAccount: From 2d125f5b1d0907a786c5360102f6f3153a6adc73 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 11:50:28 -0500 Subject: [PATCH 17/20] entrypoint: Set a flag to add local gateway IP to LOADBALANCER_IPS. This is helpful for Kubernetes, where the health checks will be NAT'd to come from the local network gateway. --- entrypoint.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/entrypoint.sh b/entrypoint.sh index c4833617e4..dcb3446220 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -30,6 +30,7 @@ DISABLE_HTTPS="${DISABLE_HTTPS:-false}" NGINX_WORKERS="${NGINX_WORKERS:-2}" NGINX_PROXY_BUFFERING="${NGINX_PROXY_BUFFERING:-off}" NGINX_MAX_UPLOAD_SIZE="${NGINX_MAX_UPLOAD_SIZE:-80m}" +TRUST_GATEWAY_IP="${TRUST_GATEWAY_IP:-False}" # Zulip certificate parameters SSL_CERTIFICATE_GENERATION="${SSL_CERTIFICATE_GENERATION:self-signed}" # Zulip related settings @@ -141,6 +142,11 @@ puppetConfiguration() { crudini --set /etc/zulip/zulip.conf application_server queue_workers_multiprocess false fi + if [ "$TRUST_GATEWAY_IP" == "True" ] || [ "$TRUST_GATEWAY_IP" == "true" ]; then + GATEWAY_IP=$(ip route | grep default | awk '{print $3}') + echo "Trusting local network gateway $GATEWAY_IP" + LOADBALANCER_IPS="${LOADBALANCER_IPS:+$LOADBALANCER_IPS,}$GATEWAY_IP" + fi if [ -n "$LOADBALANCER_IPS" ]; then echo "Setting IPs for load balancer" crudini --set /etc/zulip/zulip.conf loadbalancer ips "${LOADBALANCER_IPS}" From bbf7f98a93984840c135a72d290168e129545226 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 11:51:43 -0500 Subject: [PATCH 18/20] helm: Healthcheck the health-check endpoint. This works around 404s when a realm has not yet been created. We make use of the new TRUST_GATEWAY_IP, since the healthcheck accesses come from the local network gateway. Fixes: #496. --- kubernetes/chart/zulip/templates/statefulset.yaml | 4 ++-- kubernetes/chart/zulip/values.yaml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/kubernetes/chart/zulip/templates/statefulset.yaml b/kubernetes/chart/zulip/templates/statefulset.yaml index d9f5bbc1b6..91470ca16e 100644 --- a/kubernetes/chart/zulip/templates/statefulset.yaml +++ b/kubernetes/chart/zulip/templates/statefulset.yaml @@ -68,7 +68,7 @@ spec: {{- if .Values.livenessProbe.enabled }} livenessProbe: httpGet: - path: / + path: /health port: http httpHeaders: - name: Host @@ -82,7 +82,7 @@ spec: {{- if .Values.startupProbe.enabled }} startupProbe: httpGet: - path: / + path: /health port: http httpHeaders: - name: Host diff --git a/kubernetes/chart/zulip/values.yaml b/kubernetes/chart/zulip/values.yaml index 7c556d369e..a508d638cf 100644 --- a/kubernetes/chart/zulip/values.yaml +++ b/kubernetes/chart/zulip/values.yaml @@ -131,6 +131,7 @@ zulip: ## certificates are managed by the Kubernetes cluster, so by ## default it's disabled inside the container. DISABLE_HTTPS: true + TRUST_GATEWAY_IP: true ## Set SSL certificate generation to self-signed because ## Kubernetes manages the client-facing SSL certs. SSL_CERTIFICATE_GENERATION: self-signed From 99e88513b35391f26d541347e875ae33eadb6ee8 Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 20:38:38 -0500 Subject: [PATCH 19/20] helm: Reorganize values.yaml env vars slightly. --- kubernetes/chart/zulip/values.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kubernetes/chart/zulip/values.yaml b/kubernetes/chart/zulip/values.yaml index a508d638cf..27d45a3de0 100644 --- a/kubernetes/chart/zulip/values.yaml +++ b/kubernetes/chart/zulip/values.yaml @@ -135,16 +135,17 @@ zulip: ## Set SSL certificate generation to self-signed because ## Kubernetes manages the client-facing SSL certs. SSL_CERTIFICATE_GENERATION: self-signed - ## Domain Zulip is hosted on. + ## Domain Zulip is hosted on; this is required SETTING_EXTERNAL_HOST: zulip.example.com - ## SMTP email password. - SECRETS_email_password: "123456789" + ## Administrator's email address; this is required SETTING_ZULIP_ADMINISTRATOR: "admin@example.com" + ## Outgoing email configuration SETTING_EMAIL_HOST: "" # e.g. smtp.example.com SETTING_EMAIL_HOST_USER: "noreply@example.com" SETTING_EMAIL_PORT: "587" SETTING_EMAIL_USE_SSL: "False" SETTING_EMAIL_USE_TLS: "True" + SECRETS_email_password: "123456789" ZULIP_AUTH_BACKENDS: "EmailAuthBackend" ## If `persistence.existingClaim` is not set, a PVC (Persistent ## Volume Claim) is generated with these specifications. From 3041213fdb8ce07cd7c4d1ffefac6535909a899c Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 11 Nov 2025 20:37:31 -0500 Subject: [PATCH 20/20] github: Lint and test the helm chart. --- .github/workflows/dockerfile.yaml | 101 +++++++++++++++++++ kubernetes/chart/zulip/ci/simple-values.yaml | 31 ++++++ 2 files changed, 132 insertions(+) create mode 100644 kubernetes/chart/zulip/ci/simple-values.yaml diff --git a/.github/workflows/dockerfile.yaml b/.github/workflows/dockerfile.yaml index b5f2951321..889a490a47 100644 --- a/.github/workflows/dockerfile.yaml +++ b/.github/workflows/dockerfile.yaml @@ -43,3 +43,104 @@ jobs: tags: ghcr.io/${{ github.repository }}:pr-${{ github.event.pull_request.number }} cache-from: type=gha cache-to: type=gha,mode=max + + helm-docs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Run helm-docs + uses: losisin/helm-docs-github-action@v1 + with: + fail-on-diff: true + + helm-test: + runs-on: ubuntu-latest + needs: + - helm-docs + - build + steps: + - name: Checkout + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v4.2.0 + with: + version: v3.17.0 + + - uses: actions/setup-python@v5.3.0 + with: + python-version: "3.x" + check-latest: true + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.7.0 + with: + version: 3.14.0 + yamllint_version: 1.37.1 + yamale_version: 6.0.0 + + - name: Set up helm repos + run: | + helm repo add groundhog2k https://groundhog2k.github.io/helm-charts/ + + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --chart-dirs kubernetes/chart \ + --target-branch ${{ github.event.repository.default_branch }}) + if [[ -n "$changed" ]]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Run chart-testing (lint) + if: steps.list-changed.outputs.changed == 'true' + run: | + ct lint --github-groups \ + --chart-dirs kubernetes/chart \ + --target-branch ${{ github.event.repository.default_branch }} \ + --lint-conf lintconf.yaml + + - name: Create kind cluster + if: steps.list-changed.outputs.changed == 'true' + uses: helm/kind-action@v1 + + - name: Log in to GHCR + if: steps.list-changed.outputs.changed == 'true' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Load image into kind + if: steps.list-changed.outputs.changed == 'true' + run: | + docker pull ghcr.io/${{ github.repository }}:pr-${{ github.event.pull_request.number }} + kind load docker-image ghcr.io/${{ github.repository }}:pr-${{ github.event.pull_request.number }} --name chart-testing + + - name: Run chart-testing (install) + id: chart-testing-install + if: steps.list-changed.outputs.changed == 'true' + run: | + ct install --github-groups \ + --chart-dirs kubernetes/chart \ + --target-branch ${{ github.event.repository.default_branch }} \ + --helm-extra-set-args "--set image.tag=pr-${{ github.event.pull_request.number }}" \ + --skip-clean-up + + - name: Fetch logs + if: always() + run: | + namespace=$(helm list --all-namespaces --output json \ + | jq -r '[.[] | select(.namespace | startswith("zulip-"))][0].namespace') + kubectl get pods -n "$namespace" + for pod in $(kubectl get pods -n "$namespace" -o name); do + kubectl describe "$pod" -n "$namespace" + done + pod=$(kubectl get pods -n "$namespace" -l app.kubernetes.io/name=zulip --output name) + kubectl -n "$namespace" logs "$pod" + kubectl -n "$namespace" exec "$pod" -c zulip -- cat /var/log/zulip/errors.log diff --git a/kubernetes/chart/zulip/ci/simple-values.yaml b/kubernetes/chart/zulip/ci/simple-values.yaml new file mode 100644 index 0000000000..034c164a51 --- /dev/null +++ b/kubernetes/chart/zulip/ci/simple-values.yaml @@ -0,0 +1,31 @@ +--- +image: + repository: ghcr.io/zulip/docker-zulip + pullPolicy: Never + # CI will pass tag: of the PR + +zulip: + password: set-secure-zulip-password + environment: + SETTING_ZULIP_ADMINISTRATOR: "admin@example.net" + SETTING_EXTERNAL_HOST: zulip.example.net + ZULIP_AUTH_BACKENDS: "EmailAuthBackend" + +memcached: + memcachedPassword: set-secure-memcached-password + +rabbitmq: + auth: + password: set-secure-rabbitmq-password + erlangCookie: set-secure-cookie-password + +redis: + auth: + password: set-secure-redis-password + +postgresql: + auth: + # # postgres admin user password + postgresqlPassword: set-secure-pg-postgres-password + # # postgres zulip user password + password: set-secure-pg-zulip-password