import groovy.io.FileType import groovy.transform.stc.ClosureParams import groovy.transform.stc.SimpleType import org.signal.buildtools.StaticIpResolver ext { autoResConfig = this.&autoResConfig } def allStringsResourceFiles(@ClosureParams(value = SimpleType.class, options = ['java.io.File']) Closure c) { file('src/main/res').eachFileRecurse(FileType.FILES) { f -> if (f.name == 'strings.xml') { c(f) } } } /** * Discovers supported languages listed as under the res/values- directory. */ def autoResConfig() { def files = [] allStringsResourceFiles { f -> files.add(f.parentFile.name) } ['en'] + files.collect { f -> f =~ /^values-([a-z]{2,3}(-r[A-Z]{2})?)$/ } .findAll { matcher -> matcher.find() } .collect { matcher -> matcher.group(1) } .sort() } task replaceEllipsis { group 'Static Files' description 'Process strings for ellipsis characters.' doLast { allStringsResourceFiles { f -> def before = f.text def after = f.text.replace('...', '…') if (before != after) { f.text = after logger.info("$f.parentFile.name/$f.name...updated") } } } } task cleanApostropheErrors { group 'Static Files' description 'Fix transifex apostrophe string errors.' doLast { allStringsResourceFiles { f -> def before = f.text def after = before.replaceAll(/([^\\=08])(')/, '$1\\\\\'') if (before != after) { f.text = after logger.info("$f.parentFile.name/$f.name...updated") } } } } task excludeNonTranslatables { group 'Static Files' description 'Remove strings that are marked "translatable"="false" or are ExtraTranslations.' doLast { def englishFile = file('src/main/res/values/strings.xml') def english = new XmlParser().parse(englishFile) def nonTranslatable = english .findAll { it['@translatable'] == 'false' } .collect { it['@name'] } .toSet() def all = english.collect { it['@name'] }.toSet() def translatable = all - nonTranslatable def inMultiline = false def endBlockName = "" allStringsResourceFiles { f -> if (f != englishFile) { def newLines = f.readLines() .collect { line -> if (!inMultiline) { def singleLineMatcher = line =~ /name="([^"]*)".*(<\/|\/>)/ if (singleLineMatcher.find()) { def name = singleLineMatcher.group(1) if (!line.contains('excludeNonTranslatables') && !translatable.contains(name)) { return " " } } else { def multilineStartMatcher = line =~ /<(.*) .?name="([^"]*)".*/ if (multilineStartMatcher.find()) { endBlockName = multilineStartMatcher.group(1) def name = multilineStartMatcher.group(2) if (!line.contains('excludeNonTranslatables') && !translatable.contains(name)) { inMultiline = true; return " " } } return line } f.write(newLines.join("\n") + "\n") } } } } task postTranslateQa { group 'Static Files' description 'Runs QA to check validity of updated strings, and ensure presence of any new languages in internal lists.' dependsOn ':qa' } task resolveStaticIps { group 'Static Files' description 'Fetches static IPs for core hosts and writes them to static-ips.gradle' doLast { def staticIpResolver = new StaticIpResolver() new File(projectDir, "static-ips.gradle").text = """ ext.service_ips='${staticIpResolver.resolveToBuildConfig("chat.signal.org")}' ext.storage_ips='${staticIpResolver.resolveToBuildConfig("storage.signal.org")}' ext.cdn_ips='${staticIpResolver.resolveToBuildConfig("cdn.signal.org")}' ext.cdn2_ips='${staticIpResolver.resolveToBuildConfig("cdn2.signal.org")}' ext.cdn3_ips='${staticIpResolver.resolveToBuildConfig("cdn3.signal.org")}' ext.kbs_ips='${staticIpResolver.resolveToBuildConfig("api.backup.signal.org")}' ext.sfu_ips='${staticIpResolver.resolveToBuildConfig("sfu.voip.signal.org")}' ext.content_proxy_ips='${staticIpResolver.resolveToBuildConfig("contentproxy.signal.org")}' ext.svr2_ips='${staticIpResolver.resolveToBuildConfig("svr2.signal.org")}' ext.cdsi_ips='${staticIpResolver.resolveToBuildConfig("cdsi.signal.org")}' """.stripIndent().trim() } } task updateStaticFilesAndQa { group 'Static Files' description 'Runs tasks to update static files. This includes translations, static IPs, and licenses. Runs QA afterwards to verify all went well. Intended to be run before cutting a release.' dependsOn replaceEllipsis, cleanApostropheErrors, excludeNonTranslatables, resolveStaticIps, postTranslateQa }