From eaf1c00d20add45085e328f1c5718605305b4e23 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Wed, 29 Apr 2026 17:35:52 +0800 Subject: [PATCH] =?UTF-8?q?docs(sdk):=20=E6=B7=BB=E5=8A=A0=20React=20Nativ?= =?UTF-8?q?e=20SDK=20=E6=96=87=E6=A1=A3=E5=92=8C=20Android/HarmonyOS=20?= =?UTF-8?q?=E5=8F=91=E7=89=88=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 XuqmGroup React Native SDK 使用文档,包含安装、初始化、HTTP客户端、IM模块、推送模块、版本管理等功能说明 - 添加 Android Gradle 发版任务脚本,支持构建发布 APK 并上传到更新服务 - 添加 HarmonyOS hvigorw 发版任务脚本,支持 HAP 包构建和上传功能 - 实现多平台版本检查、自动重连、灰度发布等发版流程自动化 - 集成商店提交、定时发布、Webhook 回调等发布后处理功能 --- scripts/Fastfile | 85 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/scripts/Fastfile b/scripts/Fastfile index 45dcdc8..5ccde01 100644 --- a/scripts/Fastfile +++ b/scripts/Fastfile @@ -34,14 +34,19 @@ platform :ios do server_url = ENV.fetch("XUQM_SERVER_URL") app_key = ENV.fetch("XUQM_APP_KEY") api_token = ENV.fetch("XUQM_API_TOKEN") + tenant_url = ENV.fetch("XUQM_TENANT_URL", "").strip scheme = ENV.fetch("XUQM_SCHEME") workspace = ENV.fetch("XUQM_WORKSPACE") bundle_id = ENV.fetch("XUQM_BUNDLE_ID", "") - store_targets = ENV.fetch("XUQM_STORE_TARGETS", "") - auto_publish = ENV.fetch("XUQM_AUTO_PUBLISH_AFTER_REVIEW", "false") == "true" - publish_immediately = ENV.fetch("XUQM_PUBLISH_IMMEDIATELY", "false") == "true" - webhook_url = ENV.fetch("XUQM_WEBHOOK_URL", "") - scheduled_at = ENV.fetch("XUQM_SCHEDULED_PUBLISH_AT", "") + tenant_cfg = xuqm_fetch_sdk_config(tenant_url, app_key, "IOS") + store_targets = ENV.fetch("XUQM_STORE_TARGETS", tenant_cfg.fetch("updateDefaultStoreTargets", "APP_STORE")) + auto_publish = ENV.fetch("XUQM_AUTO_PUBLISH_AFTER_REVIEW", tenant_cfg.fetch("updateDefaultAutoPublishAfterReview", "false")) == "true" + publish_immediately = ENV.fetch("XUQM_PUBLISH_IMMEDIATELY", tenant_cfg.fetch("updateDefaultPublishImmediately", "false")) == "true" + webhook_url = ENV.fetch("XUQM_WEBHOOK_URL", tenant_cfg.fetch("updateDefaultWebhookUrl", "")) + scheduled_at = ENV.fetch("XUQM_SCHEDULED_PUBLISH_AT", tenant_cfg.fetch("updateDefaultScheduledPublishAt", "")) + publish_mode = ENV.fetch("XUQM_PUBLISH_MODE", tenant_cfg.fetch("updateDefaultPublishMode", "")).to_s.strip.upcase + dry_run = ENV.fetch("XUQM_DRY_RUN", "false") == "true" || publish_mode == "DRY_RUN" || !tenant_cfg.fetch("updateEnabled", true) + allow_version_mismatch = ENV.fetch("XUQM_ALLOW_VERSION_MISMATCH", "false") == "true" # ── 1. Read local version ──────────────────────────────────────────── version_name = get_version_number(target: scheme) @@ -53,10 +58,24 @@ platform :ios do UI.message("Server latest versionCode: #{server_version_code}") if version_code <= server_version_code + if !allow_version_mismatch + UI.message("Local version is not greater than server latest. Please enter corrected release version info.") + version_name_input = prompt(text: "Release version name [#{version_name}]: ", ci_input: "").to_s.strip + version_code_input = prompt(text: "Release version code [#{version_code}]: ", ci_input: "").to_s.strip + version_name = version_name_input unless version_name_input.empty? + version_code = version_code_input.to_i unless version_code_input.empty? + end UI.user_error!( "Local versionCode (#{version_code}) must be greater than server (#{server_version_code}). " \ "Please bump CFBundleVersion before releasing." - ) + ) if version_code <= server_version_code + end + + if version_name != get_version_number(target: scheme) + increment_version_number(version_number: version_name) + end + if version_code != get_build_number.to_i + increment_build_number(build_number: version_code.to_s) end # ── 3. Archive & export IPA ────────────────────────────────────────── @@ -76,8 +95,48 @@ platform :ios do UI.user_error!("IPA not found in #{ipa_output}") unless ipa_path UI.success("IPA: #{ipa_path}") + if dry_run + UI.message("Dry-run summary:") + UI.message(" updateEnabled=#{tenant_cfg.fetch("updateEnabled", true)}") + UI.message(" releaseVersion=#{version_name} (#{version_code})") + UI.message(" publishMode=#{publish_mode.empty? ? "MANUAL" : publish_mode}") + UI.message(" publishImmediately=#{publish_immediately}") + UI.message(" autoPublishAfterReview=#{auto_publish}") + UI.message(" scheduledAt=#{scheduled_at.empty? ? "-" : scheduled_at}") + UI.message(" webhookUrl=#{webhook_url.empty? ? "-" : webhook_url}") + UI.message(" storeTargets=#{store_targets.empty? ? "-" : store_targets}") + UI.message("Dry-run completed. No upload performed.") + next + end + # ── 4. Upload to XuqmGroup update service ──────────────────────────── change_log = prompt(text: "Release notes: ", ci_input: "") + if publish_mode.empty? + publish_mode = if publish_immediately + "NOW" + elsif !scheduled_at.empty? + "SCHEDULED" + elsif auto_publish + "AUTO_REVIEW" + else + prompt(text: "Publish mode (MANUAL/NOW/SCHEDULED/AUTO_REVIEW) [MANUAL]: ", ci_input: "").to_s.strip.upcase + end + end + dry_run = true if publish_mode == "DRY_RUN" + if !scheduled_at.empty? + publish_immediately = false + auto_publish = false + end + if publish_mode == "SCHEDULED" && scheduled_at.empty? + scheduled_at = prompt(text: "Scheduled publish time (ISO datetime): ", ci_input: "").to_s.strip + end + if publish_mode == "NOW" + publish_immediately = true + auto_publish = false + elsif publish_mode == "AUTO_REVIEW" + auto_publish = true + publish_immediately = false + end version_id = xuqm_upload_ipa( server_url: server_url, app_key: app_key, @@ -161,6 +220,20 @@ platform :ios do # ── Private helpers ────────────────────────────────────────────────────── + private_lane :xuqm_fetch_sdk_config do |options| + tenant_url = options[:tenant_url].to_s.strip + return {} if tenant_url.empty? + + uri = URI("#{tenant_url}/api/sdk/config?appId=#{options[:app_key]}&platform=#{options[:platform]}") + req = Net::HTTP::Get.new(uri) + resp = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) } + body = JSON.parse(resp.body) + body["data"] || {} + rescue => e + UI.message("Failed to load tenant defaults: #{e.message}") + {} + end + private_lane :xuqm_get_latest_version_code do |options| uri = URI("#{options[:server_url]}/api/v1/updates/app/list?appId=#{options[:app_key]}&platform=#{options[:platform]}") req = Net::HTTP::Get.new(uri)