J'essaie d'obtenir mes versions Gradle pour demander un mot de passe à la console à l'aide d'exemples de débordement de pile
Quand j'ai une déclaration telle que:
def password = System.console().readLine("Enter keystore password ")
Quand je cours, j'obtiens l'erreur
Cannot invoke method readLine() on null object
Il semble que la console sort sous la forme null
. Ce que j'ai lu nécessite Java 6 qui, si je vais à une invite de commande et tape Java -version
J'exécute Java (TM) SE Runtime Environment (build 1.6.0_27-b07).
Ce problème est suivi dans le dépôt Github de Gradle: Impossible d'utiliser System.console () avec le démon Gradle .
Ok, la raison pour laquelle cela n'a pas fonctionné était idiote, mais juste au cas où quelqu'un d'autre le rencontrerait, je pensais que je posterais.
J'exécutais la tâche à travers Android studio et je ne savais pas que l'objet console serait toujours nul. Lors de l'exécution à partir de la ligne de commande, l'objet "commande" n'est pas nul et cela fonctionne bien .
Pour une raison quelconque, l'exécution de gradle en mode démon provoque un objet console nul. Si vous spécifiez l'indicateur de ligne de commande approprié,
./gradlew assembleRelease --no-daemon
ça va marcher.
J'ai trouvé une solution ici à https://www.timroes.de/2014/01/19/using-password-prompts-with-gradle-build-files et l'ai légèrement modifiée. Néanmoins, tous les crédits reviennent à Tim Roes!
gradle.taskGraph.whenReady { taskGraph ->
if(taskGraph.hasTask(':app:assembleRelease')) {
def storePass = ''
def keyPass = ''
if(System.console() == null) {
new SwingBuilder().edt {
dialog(modal: true, title: 'Enter password', alwaysOnTop: true, resizable: false, locationRelativeTo: null, pack: true, show: true) {
vbox { // Put everything below each other
label(text: "Please enter store passphrase:")
def input1 = passwordField()
label(text: "Please enter key passphrase:")
def input2 = passwordField()
button(defaultButton: true, text: 'OK', actionPerformed: {
storePass = input1.password;
keyPass = input2.password;
dispose();
})
}
}
}
} else {
storePass = System.console().readPassword("\nPlease enter store passphrase: ")
keyPass = System.console().readPassword("\nPlease enter key passphrase: ")
}
if(storePass.size() <= 0 || keyPass.size() <= 0) {
throw new InvalidUserDataException("You must enter the passwords to proceed.")
}
storePass = new String(storePass)
keyPass = new String(keyPass)
Android.signingConfigs.release.storePassword = storePass
Android.signingConfigs.release.keyPassword = keyPass
}
}
Quelque part dans le fichier Gradle, vous avez défini la configuration de la signature de version.
Android {
...
signingConfigs {
...
release {
storeFile file(System.getProperty("user.home")+"\\Android-key")
storePassword ''
keyAlias "standard"
keyPassword ''
}
}
...
}
(N'oubliez pas de import groovy.swing.SwingBuilder
.)
En ce qui concerne la deuxième partie, vous pouvez également consulter Comment créer un fichier apk signé avec Gradle?
Exécution de System.getConsole()
à partir de Gradle lorsque la propriété org.gradle.daemon
Est true
, ou lorsqu'elle est exécutée à partir d'un IDE = comme IntelliJ ou Android Studio il renvoie null
. Ainsi, par exemple, faire System.console().readLine()
devient impossible.
De plus, à partir de Gradle 3.0gradle.daemon
Est activé par défaut .
Ensuite, au lieu d'une solution de contournement pour utiliser System.getConsole()
Je propose une alternative, utilisez ant.input
Comme ceci:
task avoidNullOnConsole << {
ant.input(message: 'Enter keystore password:', addproperty: 'userInputPassword', defaultValue : '1234')
def password = ant.properties.userInputPassword
}
Dans ce cas, ant.input
Affiche le message
et ajoute l'entrée utilisateur dans ant.properties
En utilisant comme nom de propriété la valeur définie dans addProperty
. S'il n'y a pas d'entrée utilisateur, la valeur définie dans l'attribut default
est utilisée.
Une fois exécuté, vous pouvez obtenir l'entrée utilisateur en utilisant ant.properties.yourProperty
Ou ant.properties['yourProperty']
.
Vous pouvez vérifier le reste des attributs ant.input
Ici .
Remarque: Si vous souhaitez utiliser ant.input
Plusieurs fois, tenez compte du fait que vous ne pouvez pas remplacer la propriété existante, donc addProperty
l'attribut doit être différent pour chacun.
Jetez un œil à cet article de blog ( https://www.timroes.de/2013/09/22/handling-signing-configs-with-gradle/ ).
Il décrit plusieurs façons de gérer les configurations de signature et l'une d'entre elles est exactement votre question concernant l'entrée de la console pour le mot de passe.
Vous pouvez également exécuter votre script avec:
-Dorg.gradle.daemon = false
Une solution simple à cela consiste à vérifier la null de l'objet console:
def password = null
def console = System.console()
if (console != null) {
password = console.readLine("Enter keystore password: ")
}
Android Studio ne se plaint plus du null object
.
Pour masquer les caractères saisis, utilisez readPassword()
au lieu de readLine()
:
password = new String(console.readPassword("\nEnter key password: "))
Pour contourner ce problème, j'ai utilisé le flux d'entrée standard comme suit:
println "Enter keystore password"
def password = System.in.newReader().readLine()
créer une fonction simple pour demander un mot de passe:
import javax.swing.JOptionPane
def askPass() {
def msg = 'Enter keystore password'
if (System.console() != null) {
return System.console().readLine(msg)
} else {
return javax.swing.JOptionPane.showInputDialog(msg)
}
}
ou si vous voulez une réponse O/n:
import javax.swing.JOptionPane
def ask(msg) {
if (System.console() != null) {
return System.console().readLine(msg + ' [y/n]') == 'y'
} else {
def res = JOptionPane.showConfirmDialog(null, msg, "Confirm operation", JOptionPane.YES_NO_OPTION)
return res == JOptionPane.YES_OPTION
}
}
// usage:
task run() {
doFirst {
if (file('out.txt').exists() && !ask('overwrite output?')) {
System.exit(2)
}
}
...
}