Full CI validation (#183)
* validateDictionaries gradle task now makes use of caching for much faster builds * lowered the severity of missing translations linting rule * fixed some more errors in the translations * added linting task to the GitHub CI validation workflow * enabled GitHub CI validation on push to master
This commit is contained in:
parent
4749990d44
commit
9e46213454
8 changed files with 104 additions and 79 deletions
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
|
|
@ -1,6 +1,12 @@
|
|||
name: Build the Project
|
||||
|
||||
on: [pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "**"
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
@ -15,5 +21,7 @@ jobs:
|
|||
# validation
|
||||
- name: Validate Dictionaries
|
||||
run: ./gradlew validateDictionaries
|
||||
- name: Lint
|
||||
run: ./gradlew lint
|
||||
- name: Build Release APK
|
||||
run: ./gradlew assemble
|
||||
run: ./gradlew build
|
||||
|
|
|
|||
157
build.gradle
157
build.gradle
|
|
@ -81,6 +81,89 @@ def getReleaseVersion = { ->
|
|||
return "${getVersionName()} (${getCurrentGitHash()})"
|
||||
}
|
||||
|
||||
task validateDictionaries {
|
||||
inputs.dir fileTree(dir:'assets', excludes:['dict.properties'])
|
||||
outputs.file "${project.buildDir}/dict.validation.txt"
|
||||
|
||||
doLast {
|
||||
final String csvDelimiter = ' ' // TAB
|
||||
|
||||
String errors = ""
|
||||
int errorCount = 0
|
||||
final MAX_ERRORS = 50
|
||||
|
||||
outputs.files.singleFile.text = ""
|
||||
|
||||
inputs.getFiles().each {File file ->
|
||||
if (errorCount >= MAX_ERRORS) {
|
||||
return
|
||||
}
|
||||
|
||||
println "Validating dictionary: " + file.name
|
||||
|
||||
def geographicalName = ~"[A-Z]\\w+-[^\\n]+"
|
||||
def uniqueWords = [:]
|
||||
|
||||
int lineNumber = 0
|
||||
boolean isFileValid = true
|
||||
|
||||
file.eachLine {line ->
|
||||
if (errorCount >= MAX_ERRORS) {
|
||||
return
|
||||
}
|
||||
|
||||
lineNumber++
|
||||
|
||||
String[] parts = line.split(csvDelimiter, 2)
|
||||
String word = parts[0]
|
||||
String frequency = parts.length > 1 ? parts[1] : ""
|
||||
|
||||
if (frequency.length() > 0 && !frequency.matches("^\\d+\$")) {
|
||||
isFileValid = false
|
||||
errorCount++
|
||||
errors += "Dictionary '" + file.name + "' is invalid. Found out-of-range word frequency: '" + frequency + "' on line " + lineNumber + ". Frequency must be a non-negative integer. \n"
|
||||
}
|
||||
|
||||
if (word.matches("(\\d.+?|.+?\\d|\\d)")) {
|
||||
isFileValid = false
|
||||
errorCount++
|
||||
errors += "Dictionary '" + file.name + "' is invalid. Found numbers on line " + lineNumber + ". Please, remove all numbers.\n"
|
||||
}
|
||||
|
||||
if (word.matches("^\\P{L}+\$")) {
|
||||
isFileValid = false
|
||||
errorCount++
|
||||
errors += "Dictionary '" + file.name + "' is invalid. Found a garbage word: '" + word + "' on line " + lineNumber + ".\n"
|
||||
}
|
||||
|
||||
if (word.matches("^.\$") && !Character.isUpperCase(word.charAt(0))) {
|
||||
isFileValid = false
|
||||
errorCount++
|
||||
errors += "Dictionary '" + file.name + "' is invalid. Found a single letter: '" + word + "' on line " + lineNumber + ". Only uppercase single letters are allowed. The rest of the alphabet will be added automatically.\n"
|
||||
}
|
||||
|
||||
String uniqueWordKey = word ==~ geographicalName ? word : word.toLowerCase()
|
||||
if (uniqueWords[uniqueWordKey] != null && uniqueWords[uniqueWordKey] == true) {
|
||||
isFileValid = false
|
||||
errorCount++
|
||||
errors += "Dictionary '" + file.name + "' is invalid. Found a repeating word: '" + word + "' on line " + lineNumber + ". Ensure all words appear only once.\n"
|
||||
} else {
|
||||
uniqueWords[uniqueWordKey] = true
|
||||
}
|
||||
|
||||
if (errorCount >= MAX_ERRORS ) {
|
||||
errors += "Too many errors! Aborting.\n"
|
||||
}
|
||||
}
|
||||
|
||||
outputs.files.singleFile.text += file.name + " " + (isFileValid ? "OK" : "INVALID") + "\n"
|
||||
}
|
||||
|
||||
if (errors != "") {
|
||||
throw new GradleException(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
buildToolsVersion "33.0.0"
|
||||
|
|
@ -143,78 +226,8 @@ android {
|
|||
// signingConfig android.signingConfigs.release
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task validateDictionaries {
|
||||
inputs.dir fileTree(dir:'assets', excludes:['dict.properties'])
|
||||
|
||||
doLast {
|
||||
final String csvDelimiter = ' '
|
||||
|
||||
String errors = ""
|
||||
int errorCount = 0
|
||||
final MAX_ERRORS = 50
|
||||
|
||||
inputs.getFiles().each {File file ->
|
||||
if (errorCount >= MAX_ERRORS) {
|
||||
return
|
||||
}
|
||||
println "Validating dictionary: " + file.name
|
||||
|
||||
def geographicalName = ~"[A-Z]\\w+-[^\\n]+"
|
||||
def uniqueWords = [:]
|
||||
|
||||
int lineNumber = 0
|
||||
file.eachLine {line ->
|
||||
if (errorCount >= MAX_ERRORS) {
|
||||
return
|
||||
}
|
||||
|
||||
lineNumber++
|
||||
|
||||
String[] parts = line.split(csvDelimiter, 2)
|
||||
String word = parts[0]
|
||||
String frequency = parts.length > 1 ? parts[1] : ""
|
||||
|
||||
if (frequency.length() > 0 && !frequency.matches("^\\d+\$")) {
|
||||
errorCount++
|
||||
errors += "Dictionary '" + file.name + "' is invalid. Found out-of-range word frequency: '" + frequency + "' on line " + lineNumber + ". Frequency must be a non-negative integer. \n"
|
||||
}
|
||||
|
||||
if (word.matches("(\\d.+?|.+?\\d|\\d)")) {
|
||||
errorCount++
|
||||
errors += "Dictionary '" + file.name + "' is invalid. Found numbers on line " + lineNumber + ". Please, remove all numbers.\n"
|
||||
}
|
||||
|
||||
if (word.matches("^\\P{L}+\$")) {
|
||||
errorCount++
|
||||
errors += "Dictionary '" + file.name + "' is invalid. Found a garbage word: '" + word + "' on line " + lineNumber + ".\n"
|
||||
}
|
||||
|
||||
if (word.matches("^.\$") && !Character.isUpperCase(word.charAt(0))) {
|
||||
errorCount++
|
||||
errors += "Dictionary '" + file.name + "' is invalid. Found a single letter: '" + word + "' on line " + lineNumber + ". Only uppercase single letters are allowed. The rest of the alphabet will be added automatically.\n"
|
||||
}
|
||||
|
||||
String uniqueWordKey = word ==~ geographicalName ? word : word.toLowerCase()
|
||||
if (uniqueWords[uniqueWordKey] != null && uniqueWords[uniqueWordKey] == true) {
|
||||
errorCount++
|
||||
errors += "Dictionary '" + file.name + "' is invalid. Found a repeating word: '" + word + "' on line " + lineNumber + ". Ensure all words appear only once.\n"
|
||||
} else {
|
||||
uniqueWords[uniqueWordKey] = true
|
||||
}
|
||||
|
||||
if (errorCount >= MAX_ERRORS ) {
|
||||
errors += "Too many errors! Aborting.\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors != "") {
|
||||
throw new GradleException(errors)
|
||||
}
|
||||
applicationVariants.all { variant ->
|
||||
tasks["merge${variant.name.capitalize()}Assets"].dependsOn(validateDictionaries)
|
||||
}
|
||||
}
|
||||
|
||||
preBuild.dependsOn validateDictionaries
|
||||
preBuild.mustRunAfter validateDictionaries
|
||||
|
|
|
|||
4
lint.xml
Normal file
4
lint.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
<issue id="MissingTranslation" severity="warning" />
|
||||
</lint>
|
||||
|
|
@ -49,5 +49,5 @@
|
|||
<string name="pref_double_zero_char">Символ при многократно натисната \"0\"</string>
|
||||
<string name="char_newline">Нов ред</string>
|
||||
<string name="char_space">Интервал</string>
|
||||
<string name="add_word_field_placeholder">Напишете дума...</string>
|
||||
<string name="add_word_field_placeholder">Напишете дума…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -40,5 +40,5 @@
|
|||
<string name="char_space">Espace</string>
|
||||
<string name="char_newline">Nouvelle ligne</string>
|
||||
<string name="pref_double_zero_char">Caractère lorsque «0» est appuyé plusieurs fois</string>
|
||||
<string name="add_word_field_placeholder">Tapez un mot...</string>
|
||||
<string name="add_word_field_placeholder">Tapez un mot…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -49,5 +49,5 @@
|
|||
<string name="key_hold_key">(зажать)</string>
|
||||
<string name="key_back">Назад</string>
|
||||
<string name="key_call">Позвонить</string>
|
||||
<string name="add_word_field_placeholder">Введите слово...</string>
|
||||
<string name="add_word_field_placeholder">Введите слово…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -49,5 +49,5 @@
|
|||
<string name="dictionary_truncated">Словник успішно очищено.</string>
|
||||
<string name="dictionary_missing_go_load_it">Немає словника для мови «%1$s». Перейдіть до Налаштувань, щоб завантажити його.</string>
|
||||
<string name="dictionary_load_bad_char">Помилка завантаження. Недійсне слово «%1$s» в рядку %2$d мови «%3$s».</string>
|
||||
<string name="add_word_field_placeholder">Введіть слово...</string>
|
||||
<string name="add_word_field_placeholder">Введіть слово…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<string name="add_word_exist">Word \"%1$s\" already in the dictionary.</string>
|
||||
<string name="add_word_invalid_language" translatable="false">Cannot add a word when no language is selected.</string>
|
||||
<string name="add_word_title">Add Word</string>
|
||||
<string name="add_word_field_placeholder">Type a word...</string>
|
||||
<string name="add_word_field_placeholder">Type a word…</string>
|
||||
|
||||
<string name="pref_category_about">About</string>
|
||||
<string name="pref_category_appearance">Appearance</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue