Veritas Ep. 1: Tu primer proyecto con Gradle
Abstract
Hello my name is: Veritas
Veritas es una biblioteca de validación de datos que construiremos de manera incremental a lo largo del curso. Su objetivo es ofrecer una API clara y extensible para declarar reglas, componer validaciones y obtener resultados expresivos, facilitando su uso tanto en aplicaciones como en otras bibliotecas. Iremos viendo cómo lograr estos objetivos paso a paso.
Comenzar un proyecto desde cero puede sentirse abrumador, incluso aterrador. Muchas veces no sabemos por dónde empezar, tropezamos en el camino y nos enfrentamos a decisiones que parecen correctas en un momento, pero de las que luego podemos arrepentirnos.
Por eso, Veritas no es solo una biblioteca: es también una historia que contaremos juntos. Una historia que nos servirá de guía para recorrer el proceso completo de creación de una biblioteca desde cero.
Veritas es, en última instancia, una excusa para aprender haciendo: para experimentar, equivocarnos, corregir el rumbo y descubrir cómo se construyen bibliotecas sólidas y reutilizables en el mundo real.
Estructura inicial de proyectos del curso
Comencemos creando una organización básica para los proyectos del curso. Esta carpeta puede ser compartida entre otros proyectos; si decides no usarla, solo tendrás que adaptar las rutas en los comandos posteriores.
$dibs = 'dibs' # raíz de proyectos del curso
$scripts = 'scripts' # scripts de terminal
$path = Join-Path $dibs $scripts
New-Item -ItemType Directory -Path $path -Force | Out-Null
# Alternativas:
# (New-Item -ItemType Directory -Path $path -Force) >$null
# $null = (New-Item -ItemType Directory -Path $path -Force) ¿Qué acabamos de hacer?
-
: compone rutas de forma portable (evita problemas con separadoresJoin-PathJoin-Patho\\).// -
: crea la carpeta destino (y las intermedias si faltan).New-Item -ItemType DirectoryNew-Item -ItemType Directory-
: ruta objetivo construida con-Path $path-Path $path.Join-PathJoin-Path -
: permite repetir el comando sin error si la carpeta ya existe.-Force-Force -
es el operador de pipeline: envía la salida del comando de la izquierda como entrada al de la derecha. En||esa salida se descarta para mantener la terminal limpia.| Out-Null| Out-Null
Alternativas:o(comando) >$null(comando) >$null; en este apunte preferimos la forma con$null = (comando)$null = (comando)por ser más explícita.||
-
DIBS="dibs" # raíz de proyectos del curso
SCRIPTS="scripts" # scripts de terminal
PATH_DIR="$DIBS/$SCRIPTS"
mkdir -p "$PATH_DIR" ¿Qué acabamos de hacer?
-
: crea la ruta completa si no existe y no falla si ya está creada.mkdir -p "$DIBS/$SCRIPTS"mkdir -p "$DIBS/$SCRIPTS" -
: ruta destino donde guardaremos scripts compartidos del curso."$DIBS/$SCRIPTS""$DIBS/$SCRIPTS"
(Opcional) Integrar con Git
Si quieres versionar tu estructura desde el inicio, puedes crear el
repositorio local y el remoto en GitLab de forma automatizada. Te
sugiero ejecutar primero un ensayo con
(PowerShell) o
-WhatIf -WhatIf (Bash) para validar qué va a ocurrir.
--dry-run --dry-run
-WhatIf)
#Requires -Version 7.0
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
param(
[ValidateNotNullOrEmpty()]
[Parameter(Mandatory)]
[string] $User,
[ValidateNotNullOrEmpty()]
[string] $RepositoryDirectory = $PWD.Path,
[string] $RepositoryName,
[string] $Prefix
)
$ErrorActionPreference = 'Stop'
# Normalizar ruta (soporta relativa) y validar que sea carpeta
$RepositoryDirectory = (Resolve-Path -LiteralPath $RepositoryDirectory -ErrorAction Stop).Path
$repoDirInfo = Get-Item -LiteralPath $RepositoryDirectory
if (-not $repoDirInfo.PSIsContainer) {
throw "The path '$RepositoryDirectory' is not a directory."
}
# Derivar nombre/prefijo si faltan
if (-not $RepositoryName -or [string]::IsNullOrWhiteSpace($RepositoryName)) {
$RepositoryName = $repoDirInfo.Name.ToLower()
}
if (-not $Prefix -and $repoDirInfo.Parent) {
$Prefix = $repoDirInfo.Parent.Name.ToLower()
}
# Nombre final del proyecto remoto
$target = if ([string]::IsNullOrWhiteSpace($Prefix)) { $RepositoryName } else { "$Prefix-$RepositoryName" }
# Crear remoto en GitLab
if ($PSCmdlet.ShouldProcess($target, 'Create GitLab repository')) {
Push-Location -LiteralPath $repoDirInfo.FullName
try {
git init
glab repo create $target --public
if ($LASTEXITCODE) { throw "glab exited with code $LASTEXITCODE." }
git remote add origin "https://gitlab.com/$User/$target.git"
}
finally {
Pop-Location
}
} ¿Qué acabamos de hacer?
-
: asegura PowerShell 7+.#Requires -Version 7.0#Requires -Version 7.0 -
habilita[CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium')][CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium')]y-WhatIf-WhatIf. Úsalo primero con-Confirm-Confirm.-WhatIf-WhatIf -
+$RepositoryDirectory = $PWD.Path$RepositoryDirectory = $PWD.Path: acepta rutas relativas y las normaliza; luego se obtieneResolve-Path -LiteralPathResolve-Path -LiteralPathpara validar que sea carpeta.Get-ItemGet-Item -
y$RepositoryName$RepositoryNamese derivan del directorio si no los pasas; el nombre remoto final es$Prefix$Prefix(o solo el nombre si no hay prefijo).$Prefix-$RepositoryName$Prefix-$RepositoryName -
yPush-LocationPush-Location: cambian el directorio de trabajo temporalmente.Pop-LocationPop-Locationasegura que siempre se regrese al directorio original.try { ... } finally { ... }try { ... } finally { ... } -
: crea el proyecto remoto en GitLab de forma pública (cambia aglab repo create $target --publicglab repo create $target --publicsi prefieres).--private--private -
: siif ($LASTEXITCODE) { throw ... }if ($LASTEXITCODE) { throw ... }falla, se lanza una excepción para que el script termine con error.glabglab -
: agrega el remoto llamadogit remote add origin ...git remote add origin ...apuntando al repo recién creado.originorigin
--dry-run)
#!/usr/bin/env bash
set -euo pipefail
USER=""
REPO_DIR="$(pwd)"
REPO_NAME=""
PREFIX=""
DRY_RUN=false
usage() {
echo "Usage: $0 -u <user> [-d <directory>] [-n <repo_name>] [-p <prefix>] [--dry-run]"
exit 1
}
while [[ $# -gt 0 ]]; do
case $1 in
-u|--user) USER="$2"; shift 2 ;;
-d|--dir) REPO_DIR="$2"; shift 2 ;;
-n|--name) REPO_NAME="$2"; shift 2 ;;
-p|--prefix) PREFIX="$2"; shift 2 ;;
--dry-run) DRY_RUN=true; shift ;;
*) usage ;;
esac
done
if [[ -z "$USER" ]]; then
echo "Error: user is required"; usage
fi
if [[ ! -d "$REPO_DIR" ]]; then
echo "Error: '$REPO_DIR' is not a directory"
exit 1
fi
# Derivar nombre/prefijo
if [[ -z "$REPO_NAME" ]]; then
REPO_NAME="$(basename "$REPO_DIR" | tr '[:upper:]' '[:lower:]')"
fi
if [[ -z "$PREFIX" ]]; then
PARENT="$(dirname "$REPO_DIR")"
PREFIX="$(basename "$PARENT" | tr '[:upper:]' '[:lower:]')"
fi
TARGET="$REPO_NAME"
if [[ -n "$PREFIX" ]]; then
TARGET="$PREFIX-$REPO_NAME"
fi
echo "Repositorio remoto: $TARGET"
run() {
if $DRY_RUN; then
echo "[dry-run] $*"
else
eval "$@"
fi
}
(
cd "$REPO_DIR"
run "git init"
run "glab repo create $TARGET --public"
run "git remote add origin https://gitlab.com/$USER/$TARGET.git"
) ¿Qué acabamos de hacer?
-
: indica al sistema que use Bash (o la shell compatible que encuentre en el PATH).#!/usr/bin/env bash#!/usr/bin/env bash -
:set -euo pipefailset -euo pipefail-
termina el script si algún comando falla.-e-e -
termina si se usa una variable no definida.-u-u -
asegura que los fallos en pipelines no pasen desapercibidos.-o pipefail-o pipefail
-
- Bloque
: define la ayuda y parámetros válidos (usage() {}usage() {}-u,-d,-n,-p,--dry-run). -
:while [[ $# -gt 0 ]]; do case ... esacwhile [[ $# -gt 0 ]]; do case ... esacrepresenta el número de argumentos posicionales que recibió el script.$#$#- Si el script se invoca como
, entonces./script.sh -u alice -d ./repo./script.sh -u alice -d ./repovale$#$#4(cada flag y cada valor cuentan). - El bucle
se repite mientras queden argumentos por procesar.while [[ $# -gt 0 ]]while [[ $# -gt 0 ]] - Dentro,
evalúa el primer argumento (case $1 in ... esaccase $1 in ... esac), lo asigna a una variable y luego$1$1avanza, reduciendoshiftshiften 1 (o en 2 si es flag con valor).$#$#
De esta forma se recorren y procesan todos los parámetros pasados al script.
-
- Validación: si no se pasa
se muestra error. También se valida que--user--usersea un directorio existente.$REPO_DIR$REPO_DIR -
Derivación de nombres:
-
obtiene el nombre de la carpeta actual comobasenamebasename.$REPO_NAME$REPO_NAME -
ydirnamedirnamedel padre danbasenamebasename.$PREFIX$PREFIX - Ambos se transforman a minúsculas con
.tr '[:upper:]' '[:lower:]'tr '[:upper:]' '[:lower:]'
El nombre final del repo es
(o solo el nombre si no hay prefijo).$PREFIX-$REPO_NAME$PREFIX-$REPO_NAME -
- Se imprime
para mostrar qué nombre tendrá en GitLab antes de ejecutar nada."Repositorio remoto: $TARGET""Repositorio remoto: $TARGET" -
Función
:run() { }run() { }- Si está activo
, solo imprime el comando.--dry-run--dry-run - Si no, lo ejecuta realmente con
.evalevalrepresenta todos los argumentos pasados a la función.$@$@
-
- Subshell
: cambia de directorio solo dentro del bloque de paréntesis para no alterar la sesión actual.( cd "$REPO_DIR" ... )( cd "$REPO_DIR" ... ) -
Dentro del subshell:
-
inicializa el repositorio local.git initgit init -
crea el repositorio en GitLab (usaglab repo create $TARGET --publicglab repo create $TARGET --publicsi quieres).--private--private -
conecta el repo local con el remoto.git remote add origin https://gitlab.com/$USER/$TARGET.gitgit remote add origin https://gitlab.com/$USER/$TARGET.git
-
Flujo sugerido (con ensayo primero)
Set-Location 'dibs'
$gitlabUser = '<TU-USUARIO>' # <- Reemplaza aquí
.\scripts\ps1\New-GitLabRepository.ps1 -User $gitlabUser -RepositoryDirectory .\scripts -WhatIf
.\scripts\ps1\New-GitLabRepository.ps1 -User $gitlabUser -RepositoryName index -Prefix dibs -WhatIf ¿Qué acabamos de hacer?
El flag simula la ejecución sin hacer
nada. Si todo luce bien, repite los comandos sin -WhatIf -WhatIf .
-WhatIf -WhatIf
n), ya que el script ya se
encarga de eso.
cd dibs
GITLAB_USER="<TU-USUARIO>" # <- Reemplaza aquí
./scripts/sh/new-gitlab-repository.sh -u "$GITLAB_USER" -d ./scripts --dry-run
./scripts/sh/new-gitlab-repository.sh -u "$GITLAB_USER" -n index -p dibs --dry-run ¿Qué acabamos de hacer?
--dry-run --dry-run imprime los comandos en lugar de ejecutarlos.
Si luce bien, repite sin
--dry-run --dry-run .
.gitignore
.gitignore adecuado para cada repo; puede ayudarte:
toptal.com/developers/gitignore.
# Commit y push del repo 'dibs/scripts', luego agregarlo como submódulo en 'dibs/index'
Push-Location '.\scripts'
git add .
git commit -m "chore(scripts): initial scaffolding and GitLab integration utilities"
git push -u origin main
Pop-Location
# Commit y push del repo 'dibs/index'
git submodule add "https://gitlab.com/$gitlabUser/dibs-scripts.git" scripts
git add .gitmodules scripts
git commit -m "chore(index): add scripts as submodule"
git push -u origin main ¿Qué acabamos de hacer?
- Primer bloque: versiona y sube el repo
dibs/scripts. - Segundo bloque: añade
dibs/scriptscomo submódulo endibs/indexy lo sube al remoto.
# Commit y push del repo 'dibs/scripts', luego agregarlo como submódulo en 'dibs/index'
(
cd scripts
git add .
git commit -m "chore(scripts): initial scaffolding and GitLab integration utilities"
git push -u origin main
)
# Commit y push del repo 'dibs/index'
git submodule add "https://gitlab.com/$GITLAB_USER/dibs-scripts.git" scripts
git add .gitmodules scripts
git commit -m "chore(index): add scripts as submodule"
git push -u origin main ¿Qué acabamos de hacer?
- El bloque entre paréntesis crea un subshell que entra a
scripts, hace commit inicial y lo sube a GitLab. - Luego, desde
dibs/index, se añade el submódulo, actualiza.gitmodulesy se sube todo al remoto.
Submódulos Git
Un submódulo es un repositorio independiente enlazado dentro de otro repositorio. Se usa cuando quieres mantener el historial y la evolución de un proyecto externo (o de otro repo tuyo) sin copiar su contenido. Así, el repo principal guarda solo una referencia fija a un commit del submódulo. Puedes actualizar esa referencia cuando quieras, pero los cambios en el submódulo se gestionan por separado.
Aprende más sobre submódulos en la documentación de Atlassian.
Inicializar un proyecto con Gradle
Crearemos un script mínimo para generar un esqueleto de proyecto donde tú
elijas. La clave es normalizar la ruta (soporta relativas) y ejecutar
gradle init en ese directorio. Recomiendo probar 1 con / -WhatIf -WhatIf antes.
--dry-run --dry-run
gradle init
El comando gradle init crea la estructura inicial de un proyecto
Gradle de manera guiada: puedes elegir tipo de proyecto (aplicación, biblioteca,
etc.), DSL (Kotlin o
Groovy) y nombre del proyecto, entre otros.
#Requires -Version 7.0
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
param(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string] $ProjectRoot,
[string[]] $GradleOptions = @()
)
$ErrorActionPreference = 'Stop'
# Normalize the absolute path based on the current directory
$root = `
if ([IO.Path]::IsPathRooted($ProjectRoot)) { $ProjectRoot } `
else { Join-Path -Path $PWD.Path -ChildPath $ProjectRoot }
# Create directory if missing
if (-not (Test-Path -LiteralPath $root -PathType Container)) {
if ($PSCmdlet.ShouldProcess($root, 'Create directory')) {
New-Item -ItemType Directory -Path $root -Force | Out-Null
}
}
# Run gradle init in the correct path
$gradle = (Get-Command gradle -ErrorAction Stop).Source
if ($PSCmdlet.ShouldProcess($root, 'gradle init')) {
Write-Verbose "Running 'gradle init' in '$root'"
& $gradle -p $root init @GradleOptions
if ($LASTEXITCODE) { throw "Gradle exited with code $LASTEXITCODE." }
} ¿Qué acabamos de hacer?
Esta explicación es más larga a propósito: es nuestro primer script y queremos revisar cada detalle con calma. Más adelante iremos omitiendo lo que ya hayamos aprendido.
-
: asegura que el script se ejecute con PowerShell 7+. 2#Requires -Version 7.0#Requires -Version 7.0 -
: habilita ensayos seguros con[CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium')][CmdletBinding(SupportsShouldProcess, ConfirmImpact='Medium')](simula) y-WhatIf-WhatIf(pide confirmación). 3-Confirm-Confirm -
: define los parámetros de entrada.param(...)param(...)es la carpeta del proyecto (obligatoria).$ProjectRoot$ProjectRootpermite pasar banderas extra a$GradleOptions$GradleOptions. 4gradle initgradle init -
: si algo falla, se detiene inmediatamente.$ErrorActionPreference = 'Stop'$ErrorActionPreference = 'Stop' -
- Comprueba si la carpeta existe con
y solo la crea si falta. 6Test-PathTest-Path -
: localiza el ejecutable de Gradle en el sistema. 7$gradle = (Get-Command gradle -ErrorAction Stop).Source$gradle = (Get-Command gradle -ErrorAction Stop).Source -
: ejecuta& $gradle -p $root init @GradleOptions& $gradle -p $root init @GradleOptionsgradle initen la carpeta correcta. 8 -
: verifica que Gradle terminó con éxito (0). 9$LASTEXITCODE$LASTEXITCODE -
#!/usr/bin/env bash
set -euo pipefail
PROJECT_ROOT="${1:-}"
shift || true
GRADLE_OPTS=( "$@" )
if [[ -z "${PROJECT_ROOT}" ]]; then
echo "Usage: initialize-gradle-project.sh <project-root> [gradle options...]" >&2
exit 1
fi
# Normalize to absolute path
case "${PROJECT_ROOT}" in
/*|?:/*) ROOT="${PROJECT_ROOT}" ;; # Unix abs or Windows drive abs (e.g., C:/)
*) ROOT="${PWD}/${PROJECT_ROOT}" ;;
esac
run () {
if [[ "${DRY_RUN:-false}" == "true" ]]; then
echo "[dry-run] $*"
else
eval "$@"
fi
}
# Create directory if missing
if [[ ! -d "${ROOT}" ]]; then
run "mkdir -p "${ROOT}""
fi
# Ensure gradle exists
if ! command -v gradle >/dev/null 2>&1; then
echo "Gradle not found in PATH" >&2
exit 127
fi
# Run gradle init in the correct path
run "gradle -p "${ROOT}" init ${GRADLE_OPTS[*]}" ¿Qué acabamos de hacer?
Esta explicación es más larga a propósito: es nuestro primer script y queremos revisar cada detalle con calma. Más adelante iremos omitiendo lo que ya hayamos aprendido.
-
: indica al sistema que use Bash (o la shell compatible que encuentre en el PATH).#!/usr/bin/env bash#!/usr/bin/env bash -
refuerza la seguridad del script:set -euo pipefailset -euo pipefail-
: termina de inmediato si un comando falla.-e-e -
: falla si se usa una variable no definida.-u-u -
: en un pipeline, propaga el error del primer comando que falle en lugar de ocultarlo.-o pipefail-o pipefail
-
-
: guarda el primer argumento del script en la variablePROJECT_ROOT="${1:-}"PROJECT_ROOT="${1:-}".PROJECT_ROOTPROJECT_ROOTrepresenta el primer parámetro recibido al ejecutar el script. La sintaxis$1$1indica “usa este valor, o cadena vacía si no existe”. Así evitamos que la variable quede sin definir cuando no se pasa ningún argumento.:-:- -
avanza los argumentos posicionales una posición (descartashift || trueshift || true, ya usado como$1$1). ElPROJECT_ROOTPROJECT_ROOTevita que el script termine con error si ya no hay más argumentos que desplazar.|| true|| true -
guarda todos los argumentos restantes en un arreglo llamadoGRADLE_OPTS=( "$@" )GRADLE_OPTS=( "$@" ). La sintaxisGRADLE_OPTSGRADLE_OPTSpreserva cada argumento como un elemento separado (respetando espacios). Así, cualquier flag adicional se pasa intacta luego a"$@""$@".gradle initgradle init -
valida que la variableif [[ -z "${PROJECT_ROOT}" ]]; then ... fiif [[ -z "${PROJECT_ROOT}" ]]; then ... fino esté vacía.PROJECT_ROOTPROJECT_ROOTes la forma moderna de test en Bash, más robusta que[[ ... ]][[ ... ]].[ ... ][ ... ]comprueba si la cadena está vacía.-z-zredirige el mensaje de error al flujo estándar de errores (stderr) en lugar de stdout, para distinguirlo de la salida normal.>&2>&2 - Normaliza la ruta del proyecto usando
.$PWD$PWDes una variable automática de Bash que siempre contiene la ruta del directorio actual desde donde ejecutas el script. Si$PWD$PWDes relativo, se anteponePROJECT_ROOTPROJECT_ROOTpara convertirlo en absoluto y asegurar que el proyecto se cree en la carpeta correcta.$PWD$PWD -
evalúa un valor contra varios patrones de texto. Es parecido a una cadena decase ... in ... esaccase ... in ... esacif / else ifque compara con expresiones regulares simples, no equivale a un pattern matching completo como en Scala o Python.-
: si el valor empieza con/*|?:/*)/*|?:/*)/(ruta absoluta en Unix) o conC:/(ruta absoluta en Windows), se toma tal cual ().ROOT=${PROJECT_ROOT}ROOT=${PROJECT_ROOT} -
: el asterisco es un comodín que captura cualquier otro caso. Aquí tratamos la ruta como relativa y la volvemos absoluta anteponiendo*)*)(directorio actual en Bash).$PWD$PWD
marca el final de cada rama dentro del bloque;;;;case. -
- Ensayo seguro: activa
para ver qué haría el script sin ejecutar cambios reales.DRY_RUN=trueDRY_RUN=true - Ejecuta
en el directorio correcto sin moverte de carpeta.gradle -p "$ROOT" init ${GRADLE_OPTS[*]}gradle -p "$ROOT" init ${GRADLE_OPTS[*]}
.scriptsps1Initialize-GradleProject.ps1 -ProjectRoot veritas -WhatIf ¿Qué acabamos de hacer?
¿Qué acabamos de hacer?
Notas
- Volver
Al usar
con rutas relativas, PowerShell puede resolverlas respecto a otra base (p. ej., el perfil). ConDirectoryInfoDirectoryInfoanclamos la ruta al directorio actual y evitamos crear el proyecto en otra ubicación.Join-Path -Path $PWD.Path -ChildPath ...Join-Path -Path $PWD.Path -ChildPath ...El error original apareció al no usar
:Join-PathJoin-PathWhat if: Performing the operation "Create Directory" on target "Destination: C:\Users\usuario\veritas". What if: Performing the operation "gradle init" on target "C:\Users\usuario\veritas".Gracias a
/-WhatIf-WhatIf, detectamos al instante que apuntaba a--dry-run--dry-runC:\Users\...en vez deE:\...\Dibs. El ensayo reveló el fallo y la corrección segura fue normalizar la ruta antes degradle init. -
Puedes ajustar
a otra versión, pero no garantizamos compatibilidad con PowerShell anterior. Volver#Requires#Requires -
indica que la acción es importante y puede requerir confirmación en entornos sensibles. VolverConfirmImpact='Medium'ConfirmImpact='Medium' -
-
marca[Parameter(Mandatory)][Parameter(Mandatory)]como obligatorio.$ProjectRoot$ProjectRoot -
asegura que no sea nulo o vacío.[ValidateNotNullOrEmpty()][ValidateNotNullOrEmpty()] -
: tipo de dato de la variable.[string][string]
Ejemplo de uso:
Volver..\Initialize-GradleProject.ps1 -ProjectRoot foo -GradleOptions "--type kotlin-application","--dsl kotlin".\Initialize-GradleProject.ps1 -ProjectRoot foo -GradleOptions "--type kotlin-application","--dsl kotlin" -
-
es una variable automática que guarda el directorio actual. Volver$PWD$PWD -
evita que caracteres especiales se interpreten como comodines.-LiteralPath-LiteralPathasegura que sea carpeta, no archivo.-PathType Container-PathType Containerpermite ensayar acciones con$PSCmdlet.ShouldProcess$PSCmdlet.ShouldProcesso-WhatIf-WhatIf. Volver-Confirm-Confirm -
encuentra el ejecutable real, aunque haya alias.Get-CommandGet-Commandcorta si no se encuentra. Volver-ErrorAction Stop-ErrorAction Stop -
es el call operator: ejecuta la ruta guardada en&&.$gradle$gradlefija el directorio del proyecto.-p-pusa splatting para expandir el arreglo como argumentos separados. Volver@GradleOptions@GradleOptions - Por convención, 0 significa éxito; cualquier otro código indica error. Volver
-
habilita parámetros comunes como[CmdletBinding()][CmdletBinding()]y-Verbose-Verbose. Volver-Debug-Debug