Objeto de consola null de construcción de Gradle
android-gradle build.gradle (10)
Eche un vistazo a esta publicación de blog ( https://www.timroes.de/2013/09/22/handling-signing-configs-with-gradle/ ).
Describe varias formas de administrar las configuraciones de firma y una de ellas es exactamente su pregunta sobre la entrada de la consola para la contraseña.
Estoy intentando hacer que mis compilaciones gradle soliciten una contraseña en la consola usando ejemplos del desbordamiento de la pila
Cuando tengo una declaración como:
def password = System.console().readLine("Enter keystore password ")
Cuando corro obtengo el error
Cannot invoke method readLine() on null object
Parece que la consola está saliendo como null
. Lo que he leído esto requiere java 6, que si voy a un símbolo del sistema y java -version
, estoy ejecutando Java (TM) SE Runtime Environment (compilación 1.6.0_27-b07).
Este problema está siendo rastreado en el repositorio Github de Gradle: No se puede usar System.console () con el Gradle Daemon .
Ejecutando System.getConsole()
desde Gradle cuando la propiedad org.gradle.daemon
es true
, o cuando se ejecuta desde un IDE como IntelliJ o Android Studio devuelve null
. Entonces, por ejemplo, System.console().readLine()
no es posible.
Además, a partir de Gradle 3.0 gradle.daemon
está activado de manera predeterminada .
Luego, en lugar de una solución alternativa para usar System.getConsole()
propongo una alternativa, use ant.input
manera:
task avoidNullOnConsole << {
ant.input(message: ''Enter keystore password:'', addproperty: ''userInputPassword'', defaultValue : ''1234'')
def password = ant.properties.userInputPassword
}
En este caso, ant.input
muestra el message
y agrega la entrada del usuario en ant.properties
usando como nombre de propiedad el valor definido en addProperty
. Si no hay entrada de usuario, entonces se usa el valor definido en default
atributo default
.
Una vez ejecutado, puede obtener la entrada del usuario utilizando ant.properties.yourProperty
o ant.properties[''yourProperty'']
.
Puede consultar el resto de los atributos ant.input
aquí .
Nota: Si desea utilizar ant.input
varias veces, tenga en cuenta que no puede anular y la propiedad existente, por addProperty
atributo addProperty
debe ser diferente para cada uno.
Encontré una solución aquí en https://www.timroes.de/2014/01/19/using-password-prompts-with-gradle-build-files y la modifiqué ligeramente. Sin embargo, todos los créditos van a 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
}
}
En algún lugar del archivo de algún gradle, tiene definida la configuración para la firma del lanzamiento.
android {
...
signingConfigs {
...
release {
storeFile file(System.getProperty("user.home")+"//android-key")
storePassword ''''
keyAlias "standard"
keyPassword ''''
}
}
...
}
(No olvide import groovy.swing.SwingBuilder
).
Con respecto a la segunda parte, también puede echarle un vistazo a ¿Cómo crear un archivo apk de lanzamiento firmado usando Gradle?
Ok, la razón por la que esto no funcionó fue una tontería, pero por si acaso alguien más se entera, pensé en publicarla.
Estaba ejecutando la tarea a través de Android Studio y no me di cuenta de que el objeto de la consola siempre sería nulo. Cuando se ejecuta desde la línea de comando, el objeto "comando" no es nulo y funciona bien.
Para solucionar este problema, utilicé el flujo de entrada estándar como siguiente:
println "Enter keystore password"
def password = System.in.newReader().readLine()
Por alguna razón, ejecutar gradle en modo daemon provoca un objeto de consola nulo. Si especifica el indicador de línea de comando apropiado,
./gradlew assembleRelease --no-daemon
Funcionará
Puedes ejecutar tu script también con:
-Dorg.gradle.daemon = falso
Una solución simple para esto es verificar el objeto de la consola para nulo:
def password = null
def console = System.console()
if (console != null) {
password = console.readLine("Enter keystore password: ")
}
Android Studio ya no tiene quejas sobre el null object
.
Para ocultar caracteres readPassword()
use readPassword()
lugar de readLine()
:
password = new String(console.readPassword("/nEnter key password: "))
crea una función simple para solicitar una contraseña:
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)
}
}
o si quieres respuesta de Y / 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)
}
}
...
}
password System.console() != null ? System.console().readLine("/ password: ") : ""