yeoman / generator

Rails-inspired generator system that provides scaffolding for your apps
http://yeoman.io
BSD 2-Clause "Simplified" License
1.21k stars 299 forks source link

yo breaks my sh/bash scripts #1068

Closed daggerok closed 6 years ago

daggerok commented 6 years ago

Hi,

I usually copy gradle / maven wrappers to my generator java project, and it worked perfectly fine in past. Since last time I have updated generator with newer wrapper, and now these scripts doesn't work at all - seems like end of line/file or something other was changed during copying/generation (chmod +x doesn't help, so issue not with file permission)

for example, after project generator

bash mvnw
: command not found
: command not found
'vnw: line 53: syntax error near unexpected token `in
'vnw: line 53: `case "`uname`" in

in mvnw file, line 53 is: case "uname" in

chmod +x mvnw
./mvnw
bash: ./mvnw: /bin/sh^M: bad interpreter: No such file or directory

To make wrapper scripts work again I have to completely re-generate them

Is that a bug or expected behavior?


Regards, Max

SBoudrias commented 6 years ago

Can you paste the content of your script? Right now there's not enough details for me to be able to help you.

If it's a copy problem, then we should identify the character who's wrongly replaced first.

daggerok commented 6 years ago

Hello, @SBoudrias

sure I can: for example this for maven and that for gradle

both scripts was generated accordingly by using next commands:

mvn -N io.takari:maven:wrapper -Dmaven=3.5.3
gradle wrapper --gradle-version=4.6

just after project was generated using yo jvm, that scripts are:

but if i will re-generate them using same commands - everything will be fine


Maksim

SBoudrias commented 6 years ago

In the case of mvnw, the error you printed is telling us that /bin/sh is not a valid interpreter (or not installed on your computer) - doesn't look like a copy issue.

SBoudrias commented 6 years ago

And about bash mvnw, I'm wondering if this forces to run this script as a bash script while it might not be bash compatible.

daggerok commented 6 years ago

but as far I'm regenerating same scripts with same /bin/sh interpreter it will works. Seems like this IS a copy problem, because while yo generator is copying, it may change for example end of line, so interpreter line could be not for example #!/bin/sh\n but something like #/bin/sh\r\n (don't know, just assuming) or vise versa... or something different

but you can reproduce it by your own (required java at least 8 to be installed):

cd /tmp/
npm i -g yo generator-jvm
yo jvm
# press enter
# press enter
cd app/

# test 1 (if you are using unix, or on windows using cygwin / mingw / ConEmu):
./mvnw
# or
./gradlew
# will produce errored result

# test 2:
bash mvnw
# or
bash gradlew
# also ERROR

# test 3:
chmod +x ./mvnw && ./mvnw
# or
chmod +x ./gradlew && ./gradlew
# ERROR

# just to see difference with generated scripts later
git init
git add .
git commit -am whatever

# if you have gradle installed locally, try re-create wrapper
gradle wrapper --gradle-version=4.6
# then test again:
./gradlew
# or
bash gradlew
# result: OK

# or if you have maven installed locally, try re-create wrapper scripts:
mvn -N io.takari:maven:wrapper -Dmaven=3.5.3
# test:
./mvnw
# or
bash mvnw
# result: OK

# also you can see difference between line endings for newly generated wrapper scripts
git diff
SBoudrias commented 6 years ago

Any chance different line ending would be caused by a .gitattribute file?

daggerok commented 6 years ago

Maybe, but I have added that .gitattributes few days ago trying to fix that problem. I can remove it

PS: I'm using mac and I have just generated new project and can show diff command output with that gradlew script and according script from yo generator, as you can see files are absolutely identical, but avry line showing as different:

yo jvm
# ... generate kotlin-spring-boot project named app
cd app/
diff gradlew ~/Documents/code/test/yo/generator-jvm/generators/app/templates/kotlin-spring-boot/gradlew
1,172c1,172
< #!/usr/bin/env sh
<
< ##############################################################################
< ##
< ##  Gradle start up script for UN*X
< ##
< ##############################################################################
<
< # Attempt to set APP_HOME
< # Resolve links: $0 may be a link
< PRG="$0"
< # Need this for relative symlinks.
< while [ -h "$PRG" ] ; do
<     ls=`ls -ld "$PRG"`
<     link=`expr "$ls" : '.*-> \(.*\)$'`
<     if expr "$link" : '/.*' > /dev/null; then
<         PRG="$link"
<     else
<         PRG=`dirname "$PRG"`"/$link"
<     fi
< done
< SAVED="`pwd`"
< cd "`dirname \"$PRG\"`/" >/dev/null
< APP_HOME="`pwd -P`"
< cd "$SAVED" >/dev/null
<
< APP_NAME="Gradle"
< APP_BASE_NAME=`basename "$0"`
<
< # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
< DEFAULT_JVM_OPTS=""
<
< # Use the maximum available, or set MAX_FD != -1 to use that value.
< MAX_FD="maximum"
<
< warn () {
<     echo "$*"
< }
<
< die () {
<     echo
<     echo "$*"
<     echo
<     exit 1
< }
<
< # OS specific support (must be 'true' or 'false').
< cygwin=false
< msys=false
< darwin=false
< nonstop=false
< case "`uname`" in
<   CYGWIN* )
<     cygwin=true
<     ;;
<   Darwin* )
<     darwin=true
<     ;;
<   MINGW* )
<     msys=true
<     ;;
<   NONSTOP* )
<     nonstop=true
<     ;;
< esac
<
< CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
<
< # Determine the Java command to use to start the JVM.
< if [ -n "$JAVA_HOME" ] ; then
<     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
<         # IBM's JDK on AIX uses strange locations for the executables
<         JAVACMD="$JAVA_HOME/jre/sh/java"
<     else
<         JAVACMD="$JAVA_HOME/bin/java"
<     fi
<     if [ ! -x "$JAVACMD" ] ; then
<         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
<
< Please set the JAVA_HOME variable in your environment to match the
< location of your Java installation."
<     fi
< else
<     JAVACMD="java"
<     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
<
< Please set the JAVA_HOME variable in your environment to match the
< location of your Java installation."
< fi
<
< # Increase the maximum file descriptors if we can.
< if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
<     MAX_FD_LIMIT=`ulimit -H -n`
<     if [ $? -eq 0 ] ; then
<         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
<             MAX_FD="$MAX_FD_LIMIT"
<         fi
<         ulimit -n $MAX_FD
<         if [ $? -ne 0 ] ; then
<             warn "Could not set maximum file descriptor limit: $MAX_FD"
<         fi
<     else
<         warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
<     fi
< fi
<
< # For Darwin, add options to specify how the application appears in the dock
< if $darwin; then
<     GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
< fi
<
< # For Cygwin, switch paths to Windows format before running java
< if $cygwin ; then
<     APP_HOME=`cygpath --path --mixed "$APP_HOME"`
<     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
<     JAVACMD=`cygpath --unix "$JAVACMD"`
<
<     # We build the pattern for arguments to be converted via cygpath
<     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
<     SEP=""
<     for dir in $ROOTDIRSRAW ; do
<         ROOTDIRS="$ROOTDIRS$SEP$dir"
<         SEP="|"
<     done
<     OURCYGPATTERN="(^($ROOTDIRS))"
<     # Add a user-defined pattern to the cygpath arguments
<     if [ "$GRADLE_CYGPATTERN" != "" ] ; then
<         OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
<     fi
<     # Now convert the arguments - kludge to limit ourselves to /bin/sh
<     i=0
<     for arg in "$@" ; do
<         CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
<         CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
<
<         if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
<             eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
<         else
<             eval `echo args$i`="\"$arg\""
<         fi
<         i=$((i+1))
<     done
<     case $i in
<         (0) set -- ;;
<         (1) set -- "$args0" ;;
<         (2) set -- "$args0" "$args1" ;;
<         (3) set -- "$args0" "$args1" "$args2" ;;
<         (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
<         (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
<         (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
<         (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
<         (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
<         (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
<     esac
< fi
<
< # Escape application args
< save () {
<     for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
<     echo " "
< }
< APP_ARGS=$(save "$@")
<
< # Collect all arguments for the java command, following the shell quoting and substitution rules
< eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
<
< # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
< if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
<   cd "$(dirname "$0")"
< fi
<
< exec "$JAVACMD" "$@"
---
> #!/usr/bin/env sh
>
> ##############################################################################
> ##
> ##  Gradle start up script for UN*X
> ##
> ##############################################################################
>
> # Attempt to set APP_HOME
> # Resolve links: $0 may be a link
> PRG="$0"
> # Need this for relative symlinks.
> while [ -h "$PRG" ] ; do
>     ls=`ls -ld "$PRG"`
>     link=`expr "$ls" : '.*-> \(.*\)$'`
>     if expr "$link" : '/.*' > /dev/null; then
>         PRG="$link"
>     else
>         PRG=`dirname "$PRG"`"/$link"
>     fi
> done
> SAVED="`pwd`"
> cd "`dirname \"$PRG\"`/" >/dev/null
> APP_HOME="`pwd -P`"
> cd "$SAVED" >/dev/null
>
> APP_NAME="Gradle"
> APP_BASE_NAME=`basename "$0"`
>
> # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
> DEFAULT_JVM_OPTS=""
>
> # Use the maximum available, or set MAX_FD != -1 to use that value.
> MAX_FD="maximum"
>
> warn () {
>     echo "$*"
> }
>
> die () {
>     echo
>     echo "$*"
>     echo
>     exit 1
> }
>
> # OS specific support (must be 'true' or 'false').
> cygwin=false
> msys=false
> darwin=false
> nonstop=false
> case "`uname`" in
>   CYGWIN* )
>     cygwin=true
>     ;;
>   Darwin* )
>     darwin=true
>     ;;
>   MINGW* )
>     msys=true
>     ;;
>   NONSTOP* )
>     nonstop=true
>     ;;
> esac
>
> CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
>
> # Determine the Java command to use to start the JVM.
> if [ -n "$JAVA_HOME" ] ; then
>     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
>         # IBM's JDK on AIX uses strange locations for the executables
>         JAVACMD="$JAVA_HOME/jre/sh/java"
>     else
>         JAVACMD="$JAVA_HOME/bin/java"
>     fi
>     if [ ! -x "$JAVACMD" ] ; then
>         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
>
> Please set the JAVA_HOME variable in your environment to match the
> location of your Java installation."
>     fi
> else
>     JAVACMD="java"
>     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
>
> Please set the JAVA_HOME variable in your environment to match the
> location of your Java installation."
> fi
>
> # Increase the maximum file descriptors if we can.
> if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
>     MAX_FD_LIMIT=`ulimit -H -n`
>     if [ $? -eq 0 ] ; then
>         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
>             MAX_FD="$MAX_FD_LIMIT"
>         fi
>         ulimit -n $MAX_FD
>         if [ $? -ne 0 ] ; then
>             warn "Could not set maximum file descriptor limit: $MAX_FD"
>         fi
>     else
>         warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
>     fi
> fi
>
> # For Darwin, add options to specify how the application appears in the dock
> if $darwin; then
>     GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
> fi
>
> # For Cygwin, switch paths to Windows format before running java
> if $cygwin ; then
>     APP_HOME=`cygpath --path --mixed "$APP_HOME"`
>     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
>     JAVACMD=`cygpath --unix "$JAVACMD"`
>
>     # We build the pattern for arguments to be converted via cygpath
>     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
>     SEP=""
>     for dir in $ROOTDIRSRAW ; do
>         ROOTDIRS="$ROOTDIRS$SEP$dir"
>         SEP="|"
>     done
>     OURCYGPATTERN="(^($ROOTDIRS))"
>     # Add a user-defined pattern to the cygpath arguments
>     if [ "$GRADLE_CYGPATTERN" != "" ] ; then
>         OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
>     fi
>     # Now convert the arguments - kludge to limit ourselves to /bin/sh
>     i=0
>     for arg in "$@" ; do
>         CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
>         CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
>
>         if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
>             eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
>         else
>             eval `echo args$i`="\"$arg\""
>         fi
>         i=$((i+1))
>     done
>     case $i in
>         (0) set -- ;;
>         (1) set -- "$args0" ;;
>         (2) set -- "$args0" "$args1" ;;
>         (3) set -- "$args0" "$args1" "$args2" ;;
>         (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
>         (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
>         (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
>         (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
>         (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
>         (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
>     esac
> fi
>
> # Escape application args
> save () {
>     for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
>     echo " "
> }
> APP_ARGS=$(save "$@")
>
> # Collect all arguments for the java command, following the shell quoting and substitution rules
> eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
>
> # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
> if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
>   cd "$(dirname "$0")"
> fi
>
> exec "$JAVACMD" "$@"

BWT: I have tried do same on windows, and on windows cmd newly generated project scripts (gradlew.bat / mvnw.cmd) are working well. as well as bash/sh scripts (gradlew / mvnw) in cygwin terminal. So I think it might be an issue with yeoman Generator this.fs.copy functionality in writing function:

file generators/app/index.js:

module.exports = class extends Generator {
  //...
  writing() {
    const projectDirectory = safety(this.props.projectDirectory);
    const projectType = this.props.projectType;
    //...
        [
          '**/*',
          '**/.*',
        ].forEach(pattern => this.fs.copy(
          this.templatePath(`${projectType}/${pattern}`),
          this.destinationPath(`${projectDirectory}`)
        ));
      //...
    }
  //...
};

Regards, Maksim

daggerok commented 6 years ago

Hello

Issue was solved after I removed and re-installed from scratch yo and generator-jvm packages. For some reasons, after just installing newer version of generator package: npm i -g generator-jvm npm showing me that version was updated, but yo itself used old version. And seems like it was root cause why script files wasn't fixed


Redards

SBoudrias commented 6 years ago

Oh, great to hear! Let me know if any further issue come up!