diff --git a/.readme/common.json b/.readme/common.json index e812ac51..0b6e4de9 100644 --- a/.readme/common.json +++ b/.readme/common.json @@ -2,13 +2,13 @@ "android_studio_latest_recommended_version_name": "Android Studio Otter 2 Feature Drop | 2025.2.2", "var_date_android_studio_latest_recommended_version_name": "2025/12/04", "android_studio_latest_recommended_file_name_of_exe": "android-studio-2025.2.2.7-windows.exe", - "android_studio_latest_recommended_download_address_of_exe": "https://redirector.gvt1.com/edgedl/android/studio/install/2025.2.2.7/android-studio-2025.2.2.7-windows.exe", + "android_studio_latest_recommended_download_address_of_exe": "https://edgedl.me.gvt1.com/edgedl/android/studio/install/2025.2.2.7/android-studio-2025.2.2.7-windows.exe", "android_studio_latest_recommended_file_size_of_exe": "1.34 GiB", "android_studio_latest_recommended_file_name_of_zip": "android-studio-2025.2.2.7-windows.zip", - "android_studio_latest_recommended_download_address_of_zip": "https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2025.2.2.7/android-studio-2025.2.2.7-windows.zip", + "android_studio_latest_recommended_download_address_of_zip": "https://edgedl.me.gvt1.com/edgedl/android/studio/ide-zips/2025.2.2.7/android-studio-2025.2.2.7-windows.zip", "android_studio_latest_recommended_file_size_of_zip": "1.35 GiB", "android_studio_latest_recommended_file_name_of_tar": "android-studio-2025.2.2.7-linux.tar.gz", - "android_studio_latest_recommended_download_address_of_tar": "https://redirector.gvt1.com/edgedl/android/studio/ide-zips/2025.2.2.7/android-studio-2025.2.2.7-linux.tar.gz", + "android_studio_latest_recommended_download_address_of_tar": "https://edgedl.me.gvt1.com/edgedl/android/studio/ide-zips/2025.2.2.7/android-studio-2025.2.2.7-linux.tar.gz", "android_studio_latest_recommended_file_size_of_tar": "1.38 GiB", "var_date_contribution_table_data_updated": "2025/09/06", "latest_rhino_engine_name_with_github_lineno_address": "[v1.8.1-SNAPSHOT](http://rhino.autojs6.com/blob/master/gradle.properties#L3)", diff --git a/.utils/fetch-and-parse-android-releases.mjs b/.utils/fetch-and-parse-android-releases.mjs index aaeccde7..f7835a5d 100644 --- a/.utils/fetch-and-parse-android-releases.mjs +++ b/.utils/fetch-and-parse-android-releases.mjs @@ -10,7 +10,7 @@ const WIKI_URL = 'https://en.wikipedia.org/wiki/Android_version_history'; const ANDROID_VC_URL = 'https://developer.android.com/reference/android/os/Build.VERSION_CODES'; const norm = (/** @type {string} */ s) => (s ?? '').trim().replace(/\s+/g, ' '); -const trimAndNormDash = (/** @type {string} */ s) => s.replace(/\s*[\u002d\u2013\u2014]\s*/g, '-'); +const trimAndNormDash = (/** @type {string} */ s) => s.replace(/\s*[\u002d\u2013\u2014](\s*N\/A)?\s*/ig, '-'); const stripSupportPrefix = (/** @type {string} */ s) => s.replace(/^(un)?supported:\s*/ig, ''); const handleCsvEmptyString = (/** @type {string} */ s) => /^-?$/.test(s) ? '""' : s; @@ -36,7 +36,7 @@ function quoteIfNeeded(o) { */ function cleanCell(cell) { const cloned = cell.clone(); - cloned.find('style,script,sup.reference').remove(); + cloned.find('style,script,sup.reference,span.sr-only').remove(); return cloned.text().trim().replace(/\s+/g, ' '); } diff --git a/.utils/scrape-and-inject-android-studio-codename-maps.mjs b/.utils/scrape-and-inject-android-studio-codename-maps.mjs index afdf37cf..89b8912b 100644 --- a/.utils/scrape-and-inject-android-studio-codename-maps.mjs +++ b/.utils/scrape-and-inject-android-studio-codename-maps.mjs @@ -127,8 +127,8 @@ function getCodenameMapLinesInfo(releases) { const codenameFromName = (name) => { // Capture "Android Studio [Feature Drop]". // zh-CN: 捕获 "Android Studio [Feature Drop]". - const m = /Android Studio\s+(.+?)\s*(?:(\s+\d+\s+)?Feature Drop)?(?=\s*\|)/i.exec(name); - return m ? m[1].trim() : null; + const m = /Android\s+Studio\s+(.+?)(\s+\d+)?(\s+Feature Drop)?(?=\s*\|)/i.exec(name); + return m?.[1]?.trim(); }; /** diff --git a/.utils/scrape-and-inject-ksp-releases-map.mjs b/.utils/scrape-and-inject-ksp-releases-map.mjs index c9e6c2e8..a5347965 100644 --- a/.utils/scrape-and-inject-ksp-releases-map.mjs +++ b/.utils/scrape-and-inject-ksp-releases-map.mjs @@ -9,6 +9,55 @@ import { updateGradleLinesData } from './utils/update-helper.mjs'; const URL = 'https://api.github.com/repos/google/ksp/releases'; const GITHUB_TOKEN = process.env.GITHUB_TOKEN; +/** + * Legacy tag: "-" + * - kotlinKey = full kotlin part + * - kspVer = last part + * + * KSP2 tag: "" + * - kotlinKey = "..Z" (and keep qualifier if exists) + * - kspVer = full part + * + * @param {string} rawTag + * @returns {{ kotlinKey: string, kspVer: string } | null} + */ +function splitKspTag(rawTag) { + const tag = String(rawTag || '').trim(); + if (!tag) return null; + + /** + * @param {string} s + * @returns {boolean} + */ + const looksLikePlainSemver = (s) => /^\d+\.\d+\.\d+(?:\.\d+)?$/.test(s); + + // Legacy: "-" + const parts = tag.split('-'); + const last = parts[parts.length - 1]; + if (parts.length >= 2 && looksLikePlainSemver(last)) { + return { + kotlinKey: parts.slice(0, -1).join('-'), + kspVer: last, + }; + } + + // KSP2: tag itself is kspVer (e.g. "2.3.4", or maybe "2.4.0-RC1") ---- + // Parse base numbers from the first segment before '-' + const base = tag.split('-', 1)[0]; + const m = /^(\d+)\.(\d+)\.(\d+)(?:\.\d+)?$/.exec(base); + if (!m) return null; + + const major = m[1]; + const minor = m[2]; + + // Keep qualifier if you want separate buckets for RC/Beta (optional) + const qualifier = tag.includes('-') ? tag.slice(tag.indexOf('-') + 1).trim() : ''; + const kotlinKeyPrefix = `${major}.${minor}.Z`; + const kotlinKey = qualifier ? `${kotlinKeyPrefix}-${qualifier}` : kotlinKeyPrefix; + + return { kotlinKey, kspVer: tag }; +} + /** * @returns {Promise} */ @@ -40,18 +89,10 @@ async function fetchKspReleases() { publishedAt: release.published_at, }); - const parts = tag.split('-'); - if (parts.length < 2) continue; - /** - * @example string - * "2.2.20-2.0.3" -> "2.2.20" - * "1.9.10-1.0.13" -> "1.9.10" - * "2.2.20-RC2-2.0.2" -> "2.2.20-RC2" - * "1.9.20-RC-1.0.13" -> "1.9.20-RC" - * @type {string} - */ - const kotlinVer = parts.slice(0, -1).join('-'); - if (kotlinVer === minToCheck) { + const parsed = splitKspTag(tag); + if (!parsed) continue; + + if (parsed.kotlinKey === minToCheck) { reached = true; break; } @@ -76,7 +117,10 @@ async function getMinKotlinVersionToCheck() { * @returns {string[]} */ function parseReleases(releases) { - if (!Array.isArray(releases)) return []; + if (!Array.isArray(releases)) { + console.warn('Failed to parse KSP releases'); + return []; + } // De-duplicate by Kotlin version, keep the latest one by release date. // zh-CN: 根据 Kotlin 版本去重, 保留发布时间最新的一条. @@ -85,11 +129,12 @@ function parseReleases(releases) { const latestByKotlin = new Map(); for (const r of releases) { const rawVer = String(r.version || '').trim(); - const parts = rawVer.split('-'); - if (parts.length < 2) continue; - - const kspVer = parts.pop(); - const KotlinVer = parts.join('-'); + const parsed = splitKspTag(rawVer); + if (!parsed) { + console.warn(`Failed to parse KSP tag "${rawVer}"`); + continue; + } + const { kotlinKey: KotlinVer, kspVer } = parsed; const d = new Date(r.publishedAt); if (Number.isNaN(d.getTime())) continue; diff --git a/.utils/scrape-and-inject-latest-gradle-wrapper.mjs b/.utils/scrape-and-inject-latest-gradle-wrapper.mjs index c9b7c54a..4f8e2686 100644 --- a/.utils/scrape-and-inject-latest-gradle-wrapper.mjs +++ b/.utils/scrape-and-inject-latest-gradle-wrapper.mjs @@ -1,5 +1,7 @@ // scrape-and-inject-latest-gradle-wrapper.mjs +import * as fs from 'node:fs'; +import * as path from 'node:path'; import { compareVersionStrings } from './utils/versioning.mjs'; import { fetchGradleReleases } from './fetch-and-parse-gradle-releases.mjs'; import { readPropertiesSync, writePropertiesSyncWithMap } from './utils/properties.mjs'; @@ -10,27 +12,24 @@ const URL_PREFIX = 'https://services.gradle.org/distributions'; /** @type {GradleReleaseConfig} */ const config = { // @Hint by SuperMonster003 on Sep 10, 2025. - // ! Limit major version to 8.x.x, to: + // ! Limit major version to 8.Y.Z, to: // ! - Legacy IDE/tooling compatibility // ! - JDK 17 constraint; Gradle 9+ requires JDK 21 // ! - Align with AGP/KGP/plugins matrix // ! - Mitigate deprecation removals // ! Set to null/remove to track latest major. // ! zh-CN: - // ! 将主版本限制在 8.x.x, 以便: + // ! 将主版本限制在 8.Y.Z, 以便: // ! - 兼容旧版 IDE/Tooling API // ! - 满足 JDK 17 运行时约束, 避免 Gradle 9+ 需 JDK 21 // ! - 与 AGP/KGP/第三方插件版本矩阵匹配 // ! - 降低弃用 API 移除导致的构建中断风险 // ! 如需跟随最新主版本, 将 majorVersionLimit 设为 null 或移除此项. - majorVersionLimit: '8.x.x', + // # majorVersionLimit: '8.Y.Z', + majorVersionLimit: null, format: 'bin', }; -function isVersionLimited() { - return !config.majorVersionLimit || String(config.majorVersionLimit).match(/([?x])(\.\1)*/i); -} - /** * @param {GradleRelease[]} releases * @param {string} majorVersionLimit @@ -57,14 +56,24 @@ function getLatestGradleUrl(latestGradleVersion) { /** * @param {Object} data + * @param {string} [data.fileName='gradle-wrapper.properties'] + * @param {string} [data.dirName='../gradle/wrapper'] * @param {string} data.latestGradleVersion * @param {string} data.latestGradleUrl - * @param {string} data.majorVersionLimit + * @param {string|number} data.rawMajorVersionLimit * @returns {Promise} */ -async function updateGradleWrapperFileContent({ latestGradleVersion, latestGradleUrl, majorVersionLimit }) { - const fileName = 'gradle-wrapper.properties'; - const path = '../gradle/wrapper/' + fileName; +async function updateGradleWrapperFileContent( + { + fileName = 'gradle-wrapper.properties', + dirName = '../gradle/wrapper', + latestGradleVersion, + latestGradleUrl, + rawMajorVersionLimit, + }, +) { + const path = `${dirName.replace(/\/?$/, '/')}${fileName}`; + const majorVersionLimit = parseMajorVersionLimit(rawMajorVersionLimit); const messages = []; const props = readPropertiesSync(path); let propUrl = props.get(KEY); @@ -81,11 +90,11 @@ async function updateGradleWrapperFileContent({ latestGradleVersion, latestGradl } if (compareVersionStrings(propVersion, majorVersionLimit) > 0) { - const suffix = ` (downgrade, limited by "${config.majorVersionLimit}")`; + const suffix = ` (downgrade, limited by "${rawMajorVersionLimit}")`; messages.push(`-- ${propUrl}\n-> ${latestGradleUrl}${suffix}`); props.set(KEY, propUrl.replace(re, `$1${latestGradleVersion}$3`)); } else if (compareVersionStrings(propVersion, latestGradleVersion) < 0) { - const suffix = isVersionLimited() ? ` (upgrade, but limited by "${config.majorVersionLimit}")` : ` (upgrade)`; + const suffix = ` (upgrade)`; messages.push(`-- ${propUrl}\n-> ${latestGradleUrl}${suffix}`); props.set(KEY, propUrl.replace(re, `$1${latestGradleVersion}$3`)); } @@ -109,12 +118,16 @@ async function updateGradleWrapperFileContent({ latestGradleVersion, latestGradl } } -function parseMajorVersionLimit() { - const majorVersionLimit = config.majorVersionLimit ?? 'x.x.x'; +/** + * @param {string|number} rawMajorVersionLimit + * @returns {string} + */ +function parseMajorVersionLimit(rawMajorVersionLimit) { + const majorVersionLimit = rawMajorVersionLimit ?? 'x.y.z'; const limits = String(majorVersionLimit).split('.'); for (let i = 0; i < limits.length; i++) { let limit = limits[i]; - if (limit.match(/([?x])(\.\1)*/i)) { + if (limit.match(/([xyz*?])(\.\1)*/i)) { limits[i] = limit = '9'.repeat(9); } if (isNaN(parseInt(limit))) { @@ -124,18 +137,55 @@ function parseMajorVersionLimit() { return limits.join('.'); } +/** + * @param {string} wrappersPath + * @returns {Array<{dirName: string, rawMajorVersionLimit: string}>} + */ +function listMatchingSubdirectories(wrappersPath) { + const pattern = /^g(\d+)$/; + const fullWrappersPath = path.resolve(wrappersPath); + + if (!fs.existsSync(fullWrappersPath)) { + console.warn(`Wrappers directory not found: ${fullWrappersPath}`); + return []; + } + + const results = []; + const entries = fs.readdirSync(fullWrappersPath, { withFileTypes: true }); + entries.forEach(entry => { + if (!entry.isDirectory()) return; + const matched = pattern.exec(entry.name); + const version = matched?.[1] ?? null; + if (version === null) return; + results.push({ + dirName: `${wrappersPath.replace(/\/?$/, '/')}${entry.name}/gradle/wrapper`, + rawMajorVersionLimit: `${version}.y.z`, + }); + }); + return results; +} + (async function main() { const releases = await fetchGradleReleases(); if (!releases || releases.length === 0) { throw new Error('No Gradle releases found'); } - const majorVersionLimit = parseMajorVersionLimit(); - const latestRelease = getLatestRelease(releases, majorVersionLimit); - const latestGradleVersion = latestRelease.versionName; - const latestGradleUrl = getLatestGradleUrl(latestGradleVersion); - await updateGradleWrapperFileContent({ - latestGradleVersion, latestGradleUrl, majorVersionLimit, - }); + const wrapperDir = '../gradle/wrapper'; + const modernWrappersDir = '../gradle/wrappers'; + + const candidates = [ { + dirName: wrapperDir, + rawMajorVersionLimit: config.majorVersionLimit, + } ].concat(listMatchingSubdirectories(modernWrappersDir)); + + for (const { dirName, rawMajorVersionLimit } of candidates) { + const latestRelease = getLatestRelease(releases, parseMajorVersionLimit(rawMajorVersionLimit)); + const latestGradleVersion = latestRelease.versionName; + const latestGradleUrl = getLatestGradleUrl(latestGradleVersion); + await updateGradleWrapperFileContent({ + dirName, latestGradleVersion, latestGradleUrl, rawMajorVersionLimit, + }); + } })().catch(err => { console.error('Failed to scrape or inject latest Gradle wrapper:', err); process.exit(1); diff --git a/.utils/scrape-and-update-android-releases.mjs b/.utils/scrape-and-update-android-releases.mjs index 82c07c89..f4a1c173 100644 --- a/.utils/scrape-and-update-android-releases.mjs +++ b/.utils/scrape-and-update-android-releases.mjs @@ -48,7 +48,7 @@ function updateCsv(data) { * @param {object} options * @param {boolean} [options.showIndex=false] * @param {{ mode: 'all' | 'not-all-numbers' | 'none', style: 'single' | 'double' }} [options.quote] - * @param {{ key: string, compare: (a: AndroidReleaseCsv, b: AndroidReleaseCsv) => number } | { key: string, direction: 'asc' | 'desc' }} [options.sortBy] + * @param {{ key: string, compare?: (a: AndroidReleaseCsv, b: AndroidReleaseCsv) => number, direction?: 'asc' | 'desc' }} [options.sortBy] * @param {{ [key: string]: string }} [options.headerMap] * @param {'lower' | 'upper' | 'snake' | 'kebab' | 'title' | 'camel' | 'pascal' | null} [options.headerCase] * @param {string[]} [options.headers] diff --git a/gradle/data/agp-releases.list b/gradle/data/agp-releases.list index 30de421f..4a7a365d 100644 --- a/gradle/data/agp-releases.list +++ b/gradle/data/agp-releases.list @@ -1,6 +1,7 @@ -#Sun Dec 07 19:51:46 GMT+8 2025 -9.0.0-beta03 -8.13.1 +#Sun Dec 21 20:53:47 GMT+8 2025 +9.1.0-alpha01 +9.0.0-rc01 +8.13.2 8.12.3 8.11.2 8.10.1 diff --git a/gradle/data/android-studio-build-version.properties b/gradle/data/android-studio-build-version.properties index 20bd9c25..706c9f90 100644 --- a/gradle/data/android-studio-build-version.properties +++ b/gradle/data/android-studio-build-version.properties @@ -1,4 +1,8 @@ -#Sun Dec 07 19:51:05 GMT+8 2025 +#Sun Dec 21 20:52:44 GMT+8 2025 +253.28294.334.2531.14612490=2025.3.1.1 +252.28238.7.2523.14608894=2025.2.3.6 +252.28238.7.2523.14575018=2025.2.3.5 +252.28238.7.2523.14542061=2025.2.3.4 252.28238.7.2523.14499447=2025.2.3.3 252.27397.103.2523.14473358=2025.2.3.2 252.27397.103.2523.14431943=2025.2.3.1 diff --git a/gradle/data/android-studio-codename-version.properties b/gradle/data/android-studio-codename-version.properties index 725e50d1..12251bae 100644 --- a/gradle/data/android-studio-codename-version.properties +++ b/gradle/data/android-studio-codename-version.properties @@ -1,4 +1,5 @@ -#Wed Oct 01 18:32:59 GMT+8 2025 +#Sun Dec 21 20:52:44 GMT+8 2025 +2025.3=P 2025.2=O 2025.1=N 2024.3=M diff --git a/gradle/data/android-studio-codename.properties b/gradle/data/android-studio-codename.properties index 0cb4af4d..14a7cc10 100644 --- a/gradle/data/android-studio-codename.properties +++ b/gradle/data/android-studio-codename.properties @@ -1,4 +1,6 @@ -#Wed Oct 01 20:46:54 GMT+8 2025 +#Sun Dec 21 21:14:08 GMT+8 2025 +#Born on Dec 19, 2025 +P=Panda #Born on Sep 22, 2025 O=Otter #Born on Mar 19, 2025 diff --git a/gradle/data/ksp-releases.properties b/gradle/data/ksp-releases.properties index 127df2b3..35d8b1e1 100644 --- a/gradle/data/ksp-releases.properties +++ b/gradle/data/ksp-releases.properties @@ -1,4 +1,6 @@ -#Wed Oct 29 10:14:58 GMT+8 2025 +#Mon Dec 22 21:03:44 GMT+8 2025 +#Dec 17, 2025 +2.3.Z=2.3.4 #Oct 29, 2025 2.2.21=2.0.4 #Oct 18, 2025 diff --git a/settings.gradle.kts b/settings.gradle.kts index aebc04ec..92cfff64 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -229,7 +229,10 @@ pluginManagement { val split = version.split(Regex("[\\s+\\-]"), limit = 2) val numberStr = split[0] val numberParts = numberStr.split('.').map { - it.toIntOrNull() ?: throw IllegalArgumentException("Invalid version part: '$it' in version: '$version'") + when { + it.matches(Regex("[xyz*?]", RegexOption.IGNORE_CASE)) -> 0 + else -> it.toIntOrNull() + } ?: throw IllegalArgumentException("Invalid version part: '$it' in version: '$version'") } val suffixStr = split.getOrNull(1)?.trim().orEmpty() @@ -603,11 +606,14 @@ pluginManagement { } val kspVersion = run { - val ver = Version(kspReleaseProps, kotlinVersion) + val ver = Version(versionMap, kotlinVersion) val key = ver.bestMatchingKey ?: return@run null val value = ver.bestMatchingValue ?: return@run null suffix += ver.bestMatchingOperationHintSuffix - "$key-$value" + when { + key.contains(Regex("[xyz*?]", RegexOption.IGNORE_CASE)) -> value + else -> "$key-$value" + } } mapOf("version" to kspVersion, "suffix" to suffix) diff --git a/version.properties b/version.properties index 4d0adcc3..4aeedd78 100644 --- a/version.properties +++ b/version.properties @@ -1,5 +1,5 @@ -#Thu Dec 11 10:33:50 CST 2025 -BUILD_TIME=1765420430709 +#Tue Dec 23 06:37:24 CST 2025 +BUILD_TIME=1766443044399 COMPILE_SDK_VERSION=36 IMAGE_QUANT_CMAKE_VERSION=3.22.1 IMAGE_QUANT_NDK_VERSION=26.1.10909125 @@ -27,6 +27,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13 RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3 TARGET_SDK_VERSION=36 TARGET_SDK_VERSION_INRT=29 -VERSION_BUILD=3539 +VERSION_BUILD=3544 VERSION_NAME=6.7.0 Alpha12 VSCODE_EXT_REQUIRED_VERSION=1.0.8