|
| 1 | +#!/usr/bin/env bash |
| 2 | + |
| 3 | +set -Eeuo pipefail |
| 4 | + |
| 5 | +required_variables=( |
| 6 | + CORE_VERSION |
| 7 | + PLUGIN_INTERFACE_VERSION |
| 8 | + POSTGRESQL_VERSION |
| 9 | + S3_BUCKET |
| 10 | +) |
| 11 | + |
| 12 | +for variable in "${required_variables[@]}"; do |
| 13 | + if [[ -z "${!variable:-}" ]]; then |
| 14 | + echo "Missing required environment variable: ${variable}" >&2 |
| 15 | + exit 1 |
| 16 | + fi |
| 17 | +done |
| 18 | + |
| 19 | +version_pattern='^[0-9]+\.[0-9]+\.[0-9]+$' |
| 20 | +for version in "$CORE_VERSION" "$PLUGIN_INTERFACE_VERSION" "$POSTGRESQL_VERSION"; do |
| 21 | + if [[ ! "$version" =~ $version_pattern ]]; then |
| 22 | + echo "Expected a stable X.Y.Z version, received: ${version}" >&2 |
| 23 | + exit 1 |
| 24 | + fi |
| 25 | +done |
| 26 | + |
| 27 | +S3_JAR_PREFIX="${S3_JAR_PREFIX:-core-jars}" |
| 28 | +S3_JRE_PREFIX="${S3_JRE_PREFIX:-core-runtime/jre}" |
| 29 | +S3_PACKAGE_PREFIX="${S3_PACKAGE_PREFIX:-packages/v1}" |
| 30 | +S3_JAR_PREFIX="${S3_JAR_PREFIX#/}" |
| 31 | +S3_JAR_PREFIX="${S3_JAR_PREFIX%/}" |
| 32 | +S3_JRE_PREFIX="${S3_JRE_PREFIX#/}" |
| 33 | +S3_JRE_PREFIX="${S3_JRE_PREFIX%/}" |
| 34 | +S3_PACKAGE_PREFIX="${S3_PACKAGE_PREFIX#/}" |
| 35 | +S3_PACKAGE_PREFIX="${S3_PACKAGE_PREFIX%/}" |
| 36 | + |
| 37 | +ROOT_DIR="${ROOT_DIR:-$PWD}" |
| 38 | +CORE_DIR="${ROOT_DIR}/supertokens-core" |
| 39 | +PLUGIN_INTERFACE_DIR="${ROOT_DIR}/supertokens-plugin-interface" |
| 40 | +POSTGRESQL_DIR="${ROOT_DIR}/supertokens-postgresql-plugin" |
| 41 | + |
| 42 | +for path in \ |
| 43 | + "${CORE_DIR}/config.yaml" \ |
| 44 | + "${CORE_DIR}/install" \ |
| 45 | + "${CORE_DIR}/install.bat" \ |
| 46 | + "${CORE_DIR}/LICENSE.md" \ |
| 47 | + "${POSTGRESQL_DIR}/config.yaml"; do |
| 48 | + if [[ ! -f "$path" ]]; then |
| 49 | + echo "Missing package source file: ${path}" >&2 |
| 50 | + exit 1 |
| 51 | + fi |
| 52 | +done |
| 53 | + |
| 54 | +version_at_least() { |
| 55 | + [[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -1)" == "$1" ]] |
| 56 | +} |
| 57 | + |
| 58 | +if version_at_least "$CORE_VERSION" "11.0.0"; then |
| 59 | + JRE_MAJOR="21" |
| 60 | + JRE_VERSION="21.0.7" |
| 61 | +else |
| 62 | + JRE_MAJOR="15" |
| 63 | + JRE_VERSION="15.0.1" |
| 64 | +fi |
| 65 | + |
| 66 | +WORK_DIR="$(mktemp -d)" |
| 67 | +trap 'rm -rf "$WORK_DIR"' EXIT |
| 68 | + |
| 69 | +CORE_JAR_KEY="${S3_JAR_PREFIX}/supertokens-core/v${CORE_VERSION}/core-${CORE_VERSION}.jar" |
| 70 | +CLI_JAR_KEY="${S3_JAR_PREFIX}/supertokens-core/v${CORE_VERSION}/cli.jar" |
| 71 | +DOWNLOADER_JAR_KEY="${S3_JAR_PREFIX}/supertokens-core/v${CORE_VERSION}/downloader.jar" |
| 72 | +EE_JAR_KEY="${S3_JAR_PREFIX}/supertokens-core/v${CORE_VERSION}/ee.jar" |
| 73 | +PLUGIN_INTERFACE_JAR_KEY="${S3_JAR_PREFIX}/supertokens-plugin-interface/v${PLUGIN_INTERFACE_VERSION}/plugin-interface-${PLUGIN_INTERFACE_VERSION}.jar" |
| 74 | +POSTGRESQL_JAR_KEY="${S3_JAR_PREFIX}/supertokens-postgresql-plugin/v${POSTGRESQL_VERSION}/postgresql-plugin-${POSTGRESQL_VERSION}.jar" |
| 75 | +release_manifest_key="${S3_PACKAGE_PREFIX}/releases/core-${CORE_VERSION}.json" |
| 76 | + |
| 77 | +download_object() { |
| 78 | + local key="$1" |
| 79 | + local destination="$2" |
| 80 | + |
| 81 | + mkdir -p "$(dirname "$destination")" |
| 82 | + aws s3 cp "s3://${S3_BUCKET}/${key}" "$destination" --only-show-errors |
| 83 | +} |
| 84 | + |
| 85 | +object_state() { |
| 86 | + local key="$1" |
| 87 | + local error_file="${WORK_DIR}/head-object-error" |
| 88 | + |
| 89 | + if aws s3api head-object --bucket "$S3_BUCKET" --key "$key" >/dev/null 2>"$error_file"; then |
| 90 | + printf 'exists' |
| 91 | + elif grep -Eq '\((404|NoSuchKey|NotFound)\)' "$error_file"; then |
| 92 | + printf 'missing' |
| 93 | + else |
| 94 | + cat "$error_file" >&2 |
| 95 | + return 1 |
| 96 | + fi |
| 97 | +} |
| 98 | + |
| 99 | +if [[ "$(object_state "$release_manifest_key")" == "exists" ]]; then |
| 100 | + existing_release_manifest="${WORK_DIR}/existing-core-${CORE_VERSION}.json" |
| 101 | + download_object "$release_manifest_key" "$existing_release_manifest" |
| 102 | + existing_tuple="$(jq -r '[.core, .plugin, .pluginInterface] | join("/")' "$existing_release_manifest")" |
| 103 | + requested_tuple="${CORE_VERSION}/${POSTGRESQL_VERSION}/${PLUGIN_INTERFACE_VERSION}" |
| 104 | + if [[ "$existing_tuple" != "$requested_tuple" ]]; then |
| 105 | + echo "Release ${CORE_VERSION} is already published with tuple ${existing_tuple}" >&2 |
| 106 | + exit 1 |
| 107 | + fi |
| 108 | +fi |
| 109 | + |
| 110 | +package_keys='{}' |
| 111 | +platforms=(mac windows linux linux-arm) |
| 112 | + |
| 113 | +for platform in "${platforms[@]}"; do |
| 114 | + if [[ "$platform" == "linux-arm" && "$JRE_MAJOR" == "12" ]]; then |
| 115 | + continue |
| 116 | + fi |
| 117 | + |
| 118 | + staging="${WORK_DIR}/staging-${platform}" |
| 119 | + package_root="${staging}/supertokens" |
| 120 | + mkdir -p \ |
| 121 | + "${package_root}/core" \ |
| 122 | + "${package_root}/cli" \ |
| 123 | + "${package_root}/downloader" \ |
| 124 | + "${package_root}/plugin" \ |
| 125 | + "${package_root}/plugin-interface" \ |
| 126 | + "${package_root}/jre" |
| 127 | + |
| 128 | + download_object "$CORE_JAR_KEY" "${package_root}/core/core-${CORE_VERSION}.jar" |
| 129 | + download_object "$CLI_JAR_KEY" "${package_root}/cli/cli.jar" |
| 130 | + download_object "$DOWNLOADER_JAR_KEY" "${package_root}/downloader/downloader.jar" |
| 131 | + download_object "$PLUGIN_INTERFACE_JAR_KEY" "${package_root}/plugin-interface/plugin-interface-${PLUGIN_INTERFACE_VERSION}.jar" |
| 132 | + download_object "$POSTGRESQL_JAR_KEY" "${package_root}/plugin/postgresql-plugin-${POSTGRESQL_VERSION}.jar" |
| 133 | + |
| 134 | + if [[ "$(object_state "$EE_JAR_KEY")" == "exists" ]]; then |
| 135 | + if [[ ! -f "${CORE_DIR}/ee/LICENSE.md" ]]; then |
| 136 | + echo "The EE JAR exists but ${CORE_DIR}/ee/LICENSE.md is missing" >&2 |
| 137 | + exit 1 |
| 138 | + fi |
| 139 | + download_object "$EE_JAR_KEY" "${package_root}/ee/ee.jar" |
| 140 | + cp "${CORE_DIR}/ee/LICENSE.md" "${package_root}/ee/LICENSE.md" |
| 141 | + fi |
| 142 | + |
| 143 | + jre_archive_key="${S3_JRE_PREFIX}/${JRE_VERSION}/${platform}.tar.gz" |
| 144 | + jre_checksum_key="${jre_archive_key}.sha256" |
| 145 | + jre_archive="${WORK_DIR}/${JRE_VERSION}-${platform}.tar.gz" |
| 146 | + jre_checksum="${jre_archive}.sha256" |
| 147 | + download_object "$jre_archive_key" "$jre_archive" |
| 148 | + download_object "$jre_checksum_key" "$jre_checksum" |
| 149 | + expected_jre_checksum="$(cut -d ' ' -f 1 "$jre_checksum")" |
| 150 | + actual_jre_checksum="$(sha256sum "$jre_archive" | cut -d ' ' -f 1)" |
| 151 | + if [[ ! "$expected_jre_checksum" =~ ^[a-fA-F0-9]{64}$ || "$expected_jre_checksum" != "$actual_jre_checksum" ]]; then |
| 152 | + echo "JRE checksum mismatch for ${jre_archive_key}" >&2 |
| 153 | + exit 1 |
| 154 | + fi |
| 155 | + tar -xzf "$jre_archive" -C "${package_root}/jre" |
| 156 | + |
| 157 | + if [[ "$platform" == "linux" || "$platform" == "linux-arm" ]]; then |
| 158 | + java_description="$(file -Lb "${package_root}/jre/bin/java")" |
| 159 | + if [[ "$platform" == "linux" && "$java_description" != *"x86-64"* ]]; then |
| 160 | + echo "Expected an x86-64 Linux JRE, found: ${java_description}" >&2 |
| 161 | + exit 1 |
| 162 | + fi |
| 163 | + if [[ "$platform" == "linux-arm" && "$java_description" != *"aarch64"* ]]; then |
| 164 | + echo "Expected an AArch64 Linux JRE, found: ${java_description}" >&2 |
| 165 | + exit 1 |
| 166 | + fi |
| 167 | + fi |
| 168 | + |
| 169 | + install_file="install" |
| 170 | + if [[ "$platform" == "windows" ]]; then |
| 171 | + install_file="install.bat" |
| 172 | + fi |
| 173 | + cp "${CORE_DIR}/${install_file}" "${package_root}/${install_file}" |
| 174 | + chmod 755 "${package_root}/${install_file}" |
| 175 | + cp "${CORE_DIR}/LICENSE.md" "${package_root}/LICENSE.md" |
| 176 | + { |
| 177 | + cat "${CORE_DIR}/config.yaml" |
| 178 | + printf '\n\n' |
| 179 | + cat "${POSTGRESQL_DIR}/config.yaml" |
| 180 | + } > "${package_root}/config.yaml" |
| 181 | + cp "${package_root}/config.yaml" "${package_root}/config.yaml.original" |
| 182 | + cat > "${package_root}/version.yaml" <<EOF |
| 183 | +core_version: ${CORE_VERSION} |
| 184 | +plugin_interface_version: ${PLUGIN_INTERFACE_VERSION} |
| 185 | +plugin_version: ${POSTGRESQL_VERSION} |
| 186 | +plugin_name: postgresql |
| 187 | +EOF |
| 188 | + |
| 189 | + for legacy_file in INSTALLATION_INSTRUCTIONS.txt SuperTokensLicense.pdf OpenSourceLicenses.pdf; do |
| 190 | + if [[ -f "${CORE_DIR}/${legacy_file}" ]]; then |
| 191 | + cp "${CORE_DIR}/${legacy_file}" "${package_root}/${legacy_file}" |
| 192 | + fi |
| 193 | + done |
| 194 | + |
| 195 | + find "$staging" -exec touch -h -t 198001010000 {} + |
| 196 | + zip_path="${WORK_DIR}/supertokens-${platform}.zip" |
| 197 | + ( |
| 198 | + cd "$staging" |
| 199 | + find . -print | LC_ALL=C sort | zip -X -y -q "$zip_path" -@ |
| 200 | + ) |
| 201 | + |
| 202 | + package_key="${S3_PACKAGE_PREFIX}/core-${CORE_VERSION}/${platform}.zip" |
| 203 | + checksum="$(sha256sum "$zip_path" | cut -d ' ' -f 1)" |
| 204 | + checksum_path="${WORK_DIR}/${platform}.sha256" |
| 205 | + manifest_path="${WORK_DIR}/${platform}.manifest.json" |
| 206 | + printf '%s %s.zip\n' "$checksum" "$platform" > "$checksum_path" |
| 207 | + jq -n \ |
| 208 | + --arg packageKey "$package_key" \ |
| 209 | + --arg sha256 "$checksum" \ |
| 210 | + --arg core "$CORE_VERSION" \ |
| 211 | + --arg plugin "$POSTGRESQL_VERSION" \ |
| 212 | + --arg pluginInterface "$PLUGIN_INTERFACE_VERSION" \ |
| 213 | + --arg os "$platform" \ |
| 214 | + --arg jreVersion "$JRE_VERSION" \ |
| 215 | + --arg coreJarKey "$CORE_JAR_KEY" \ |
| 216 | + --arg cliJarKey "$CLI_JAR_KEY" \ |
| 217 | + --arg downloaderJarKey "$DOWNLOADER_JAR_KEY" \ |
| 218 | + --arg pluginInterfaceJarKey "$PLUGIN_INTERFACE_JAR_KEY" \ |
| 219 | + --arg postgresqlJarKey "$POSTGRESQL_JAR_KEY" \ |
| 220 | + --arg jreArchiveKey "$jre_archive_key" \ |
| 221 | + --argjson size "$(stat -c %s "$zip_path")" \ |
| 222 | + '{ |
| 223 | + packageKey: $packageKey, |
| 224 | + sha256: $sha256, |
| 225 | + size: $size, |
| 226 | + tuple: { |
| 227 | + core: $core, |
| 228 | + plugin: $plugin, |
| 229 | + pluginInterface: $pluginInterface, |
| 230 | + os: $os, |
| 231 | + jreVersion: $jreVersion |
| 232 | + }, |
| 233 | + inputs: { |
| 234 | + coreJar: $coreJarKey, |
| 235 | + cliJar: $cliJarKey, |
| 236 | + downloaderJar: $downloaderJarKey, |
| 237 | + pluginInterfaceJar: $pluginInterfaceJarKey, |
| 238 | + postgresqlJar: $postgresqlJarKey, |
| 239 | + jreArchive: $jreArchiveKey |
| 240 | + } |
| 241 | + }' > "$manifest_path" |
| 242 | + |
| 243 | + aws s3 cp "$manifest_path" "s3://${S3_BUCKET}/${package_key%.zip}.manifest.json" \ |
| 244 | + --content-type application/json \ |
| 245 | + --only-show-errors |
| 246 | + aws s3 cp "$checksum_path" "s3://${S3_BUCKET}/${package_key%.zip}.sha256" \ |
| 247 | + --content-type text/plain \ |
| 248 | + --only-show-errors |
| 249 | + aws s3 cp "$zip_path" "s3://${S3_BUCKET}/${package_key}" \ |
| 250 | + --content-type application/zip \ |
| 251 | + --content-disposition "attachment; filename=supertokens-${CORE_VERSION}-${platform}.zip" \ |
| 252 | + --only-show-errors |
| 253 | + |
| 254 | + package_keys="$(jq -c --arg platform "$platform" --arg packageKey "$package_key" '. + {($platform): $packageKey}' <<< "$package_keys")" |
| 255 | +done |
| 256 | + |
| 257 | +release_manifest="${WORK_DIR}/core-${CORE_VERSION}.json" |
| 258 | +jq -n \ |
| 259 | + --arg core "$CORE_VERSION" \ |
| 260 | + --arg plugin "$POSTGRESQL_VERSION" \ |
| 261 | + --arg pluginInterface "$PLUGIN_INTERFACE_VERSION" \ |
| 262 | + --arg jreVersion "$JRE_VERSION" \ |
| 263 | + --argjson packages "$package_keys" \ |
| 264 | + '{ |
| 265 | + core: $core, |
| 266 | + plugin: $plugin, |
| 267 | + pluginInterface: $pluginInterface, |
| 268 | + jreVersion: $jreVersion, |
| 269 | + packages: $packages |
| 270 | + }' > "$release_manifest" |
| 271 | + |
| 272 | +if [[ "$(object_state "$release_manifest_key")" == "exists" ]]; then |
| 273 | + remote_release_manifest="${WORK_DIR}/remote-core-${CORE_VERSION}.json" |
| 274 | + download_object "$release_manifest_key" "$remote_release_manifest" |
| 275 | + existing_release_tuple="$(jq -r '[.core, .plugin, .pluginInterface] | join("/")' "$remote_release_manifest")" |
| 276 | + requested_release_tuple="${CORE_VERSION}/${POSTGRESQL_VERSION}/${PLUGIN_INTERFACE_VERSION}" |
| 277 | + if [[ "$existing_release_tuple" != "$requested_release_tuple" ]]; then |
| 278 | + echo "Refusing to replace ${release_manifest_key} with a different release tuple" >&2 |
| 279 | + exit 1 |
| 280 | + fi |
| 281 | +fi |
| 282 | + |
| 283 | +aws s3 cp "$release_manifest" \ |
| 284 | + "s3://${S3_BUCKET}/${release_manifest_key}" \ |
| 285 | + --content-type application/json \ |
| 286 | + --only-show-errors |
0 commit comments