Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Windows NPM #658

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open

Conversation

MetaMmodern
Copy link

@MetaMmodern MetaMmodern commented Jun 29, 2022

Description

These changes add support for Windows OS. Was tested on Windows 10 (x64), server and web-client start now.

⚠️ I am not a haskell developer!: My code may be ugly and even disguisting, but that's what I've achived by googling)

Code explanation

  1. The reason why npm was not starting is because Windows Process API appends .exe automatically to the executable filename, however npm as program does not provide any .exe file, only .cmd and .ps1. (explained here: https://stackoverflow.com/questions/43139364/createprocess-weird-behavior-with-files-without-extension) The first thing to do is to support .cmd for windows only. That's implemented in oSSpecificNpm variable. (Screenshot of ghci session for proof)
    image

  2. When we run npm.cmd from inside haskell it will complain that it can't find the npm-cli. The reason and solution is explained here: https://stackoverflow.com/a/44820337 . The implementation is made in compileOsSpecificNodeCommand Common function and in src/Wasp/Generator/Job/Process.hs file. (Screenshot as demo)
    image

Things to do further

Do NOT update docs and do not mention Windows support, since there are still following issues persistent(at least in my env):

  1. Some path's in generated js files are incorrect for windows (backslashes do not work).
    image

  2. Some issues with db and/or with encoding during db initialization/migration.
    image

  3. UI did not render for me😢.
    image

Fixes #48

Type of change

Please select the option(s) that is more relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • This change requires a documentation update

@MetaMmodern MetaMmodern changed the title add support for Windows NPM Add support for Windows NPM Jun 29, 2022
Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MetaMmodern Super awesome that we are making progress in the Windows direction!!

You did a great job taking into account that you don't know Haskell :). I left some comments, mostly it comes down to figuring out what exactly we want to do. My previous bad naming makes things a bit more complex but I think we can fix that now also.

)
where

import qualified Wasp.SemanticVersion as SV
import System.Info (os)
import Data.List
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We try to import stuff qualified or named, so we don't pollute the namespace.
Therefore, best to do import Data.List (intercalate)!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -38,3 +42,12 @@ npmVersionRange =

prismaVersion :: SV.Version
prismaVersion = SV.Version 3 15 2

oSSpecificNpm :: [Char]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job, you put this in the best place in the codebase I think :), finding your way around well I see!

Maybe we can just call this function npmCmd? The fact that it is OS specific we can leave as an implementation detail of the function itself.

A bit of Haskell: instead of [Char], normally we use String -> it is just type alias for [Char] really, but it is more common.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could also provide a short comment explaining why is it important to add this .cmd on windows, since it took us some time to figure that out, and if somebody later stumbles onto this, they might wonder why we are adding this .cmd on windows.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Martinsos for Strings--got it, did not think haskell has those) For exmplanation comment--no problem. Should I leave a text explanation or leave a link to SO?

Copy link
Member

@Martinsos Martinsos Jun 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe best to do both: text explanation + SO link! Your explanations here in description are already very good, smth very similar should do just fine.

oSSpecificNpm :: [Char]
oSSpecificNpm = "npm" ++ if os /= "mingw32" then "" else ".cmd"

compileOsSpecificNodeCommand :: [Char] -> [[Char]] -> ([Char], [[Char]])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could maybe also call this one just compileNodeCmdWithArgs, and leave the fact about OS as internal thing.

s/[Char]/String

Here we could also benefit from a comment explaining the reasoning behind the stuff we have to do for Windows. In this case it would also explain it to me since right now I don't understand why we have to do this :D! I thought node was working fine for us?

EDIT: reading code below, I realized this command here and args are not node + some args, it is instead ANY command that we somehow know requires node to be installed on the machine + its args. I suggested we rename on of the old functions below, check it out -> I gave it poor name before and I confused me now, possibly you also. So if we rename that one, we should also rename this one to compileCmdWithArgsRequiringNode.

But most important will be documenting what is the intention of this function, then further direction will be clearer.

@@ -102,7 +102,8 @@ runNodeCommandAsJob fromDir command args jobType chan = do
Right nodeVersion ->
if SV.isVersionInRange nodeVersion C.nodeVersionRange
then do
let process = (P.proc command args) {P.cwd = Just $ SP.fromAbsDir fromDir}
let (specificCommand, specificArgs) = C.compileOsSpecificNodeCommand command args
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at runNodeCommandAsJob, I am thinking that it is not clear what it actually does. This falls on me, I should have documented it better! But reading it now, I think the point is that it runs any command with args, while first checking if node is available on the machine and is using correct version?

So, it should probably be renamed to runCommandThatRequiresNodeAsJob! Maybe we can do this in this PR since we are already here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for using compileOsSpecificNodeCommand here -> We are basically applying it to any command really, not node command, just any command that for some reason requires node to be present on the machine. Is that ok, is that what we wanted? I guess I don't know because I am not sure what that cmd.exe does and why we are doing it.

If so, should we use it for all commands we are trying to run, and not just here?

What about us calling node directly, I see that below we are calling node, in line 129 -> what about that, is that ok to be left like that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so now I read what you wrote about cmd.exe and read the SO answer you linked to!
So interesting, it seems that npm.cmd script is written in such way that calling it programmatically can make it work wrong, so by adding cmd.exe /c in front we ensure it works correctly.

The question is then: Do we want to use this trick only when calling npm, or do we want to use it when calling any commands on Windows? I am thinking maybe it is enough to use it just for calling npm, at least for now?

In that case, let's use it like that, only for npm. So what we could do is rename this function to

buildNpmCmdWithArgs :: [String] -> (String, [String])

and that is it? And then we call it only in places where we need to call npm?

@Martinsos
Copy link
Member

Ah I realize now you explained in the description why we are doing the cmd.exe thing! Ok I will check it out sorry about that -> I have to run now but I should be able to get back to it in in an hour or two, I will read it then and write more educated comment!

@Martinsos
Copy link
Member

Alright, read about cmd.exe and left the comment, that is it from me for this first iteration of review!

@Martinsos
Copy link
Member

Oh btw we use ormolu for formatting Haskell code -> is that something you could easily set up, so that we can get the CI working? If not, don't worry, I can take care of that for you later.

@MetaMmodern
Copy link
Author

Oh btw we use ormolu for formatting Haskell code -> is that something you could easily set up, so that we can get the CI working? If not, don't worry, I can take care of that for you later.

Um, I'll try but not now, later evening)

@Martinsos
Copy link
Member

In the meantime I made commits that should fix the two problems that you identified @MetaMmodern (wrong path and problem with \xad) -> I pushed them onto your main branch, so make sure to pull it before you continue.
This should enable us to move forward again :)! I believe there will be another problem, which is the fact that script command does not exist on Windows, and we currently need it for smth, but let's see if that is a problem or not first and then we can consider it more.

@MetaMmodern
Copy link
Author

MetaMmodern commented Jul 2, 2022

@Martinsos for db migration similar error:
image

So wasp start starts, but because db is empty it's stuck at Loading....

For the rest review:

  1. Namings changed.
  2. Comments left.
  3. Files Linted.

(cabal dependencies installation is extremely slow, even npm is not so slow. shame on cabal)

@Martinsos
Copy link
Member

Awesome @MetaMmodern !

I left couple more comments, some are just comments/naming, one is about where to call the "buildNpmCmdWithArgs" function.

As for encoding issue with prisma db migrate -> that happens now due to us not supporting the encoding that you use in your terminal, which is CP866 (russian cyrilic).
I found this library now https://hackage.haskell.org/package/encoding-0.8.7/docs/Data-Encoding.html, once we add that we should have support for CP866 and many other encodings -> I just need to find some time to do it properly since it is not super simple due to them complicating things a bit with Monads!

Ha yes it takes long time for cabal since there is a lot to compile really, Haskell is more complex language than JS, but usually it is so slow only on the first build!

waspc/src/Wasp/Generator/Common.hs Outdated Show resolved Hide resolved

-- | Changes an npm command to a cmd.exe command on Windows only. Calling npm from API causes troubles.
-- The reason and solution exaplined here: https://stackoverflow.com/a/44820337
buildNpmCmdWithArgs :: String -> [String] -> (String, [String])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We said this is to be used only for calling npm. But in that case, we don't need command as input, right? Since we know it is npm?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

oSSpecificNpm :: String
oSSpecificNpm = "npm" ++ if os /= "mingw32" then "" else ".cmd"

-- | Changes an npm command to a cmd.exe command on Windows only. Calling npm from API causes troubles.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar like above, this is addressing implementation details. I would make sure this comment talks about what function does from the outside, now how it does it, and details can go inside of function. We might not even need this comment if it is clean what it does from the function name + signature.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved comments into implementation

errorOrNodeVersion <- getNodeVersion
case errorOrNodeVersion of
Left errorMsg -> exitWithError (ExitFailure 1) (T.pack errorMsg)
Right nodeVersion ->
if SV.isVersionInRange nodeVersion C.nodeVersionRange
then do
let process = (P.proc command args) {P.cwd = Just $ SP.fromAbsDir fromDir}
let (specificCommand, specificArgs) = C.buildNpmCmdWithArgs command args
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we renamed the function to buildNpmCmdWithArgs, and that it is used only for running npm + some args, I don't think we want to call it here anymore, since here we are dealing with any command that requires node installed, which can also be other stuff that is not npm, for example npx.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done)

Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MetaMmodern this should be it! In order to speed the merging up a bit, I fixed one tiny compiler error that somehow sneaked in, did a tiny bit of left-over refactoring, and I added more encodings, that should resolve the issue you had with CP861!

Would you like to give it a finally try, to see if the issue with CP861 is now resolved?

@MetaMmodern
Copy link
Author

@MetaMmodern this should be it! In order to speed the merging up a bit, I fixed one tiny compiler error that somehow sneaked in, did a tiny bit of left-over refactoring, and I added more encodings, that should resolve the issue you had with CP861!

Would you like to give it a finally try, to see if the issue with CP861 is now resolved?

in an hour or so) lil bit later

@Martinsos
Copy link
Member

@MetaMmodern I think I found the reason -> there was this flag for this new encoding package that sometimes causes problems. I turned it off since we don't use it -> let's see now if it helps.
What sucks is that encodings package takes really long time to build, 5-10 minutes, I am not sure why, it is reported on their GH repo as an issue but not resolved yet. But there aren't really any better alternatives out there.

TODO for myself: maybe try with the knob package -> wrap bytestrings into file handlers and let withUtf8 handle it. But does withUtf8 support wide array of encodings or not?

@MetaMmodern
Copy link
Author

@Martinsos maybe there is a way to apply caching to library builds? Like separate those into some kind of .bin files and use cache if dependencies did not change)

@Martinsos
Copy link
Member

@Martinsos maybe there is a way to apply caching to library builds? Like separate those into some kind of .bin files and use cache if dependencies did not change)

Yup there is caching already in place! It just takes long this first time.

@Martinsos
Copy link
Member

It builds now @MetaMmodern

@MetaMmodern
Copy link
Author

It builds now @MetaMmodern

ok. checking locally

@MetaMmodern
Copy link
Author

@Martinsos nnnnnope)
image

@Martinsos
Copy link
Member

Martinsos commented Jul 4, 2022 via email

@MetaMmodern
Copy link
Author

Wooo great! This was unfortunately expected, but we took care of encoding
yay! This is furthest we got right?

On Mon, 4 Jul 2022, 18:06 Bunyamin Shabanov, @.***>
wrote:

@Martinsos https://github.com/Martinsos nnnnnope)
[image: image]
https://user-images.githubusercontent.com/42899786/177189747-06a070ad-33b4-41e1-90c7-192101fb550b.png


Reply to this email directly, view it on GitHub
#658 (comment), or
unsubscribe
https://github.com/notifications/unsubscribe-auth/AALXFB32RHA74DFD325MC33VSMDWXANCNFSM52GXRSUQ
.
You are receiving this because you were mentioned.Message ID:
@.***>

Well, as I understood--yes.

@Martinsos
Copy link
Member

I managed to set up my Windows to run Haskell, so now I can also test!

Ok, so I did following, as a quick hack in the place where we now use scriptand it seems to be enough to get prisma db migrate-dev to work (at least for now):

let job = runNodeDependentCommandAsJob J.Db serverDir ("cmd.exe", pure $ unwords $ "/c" : npxPrismaMigrateCmd)

I will try to properly add this to the code.

Regarding your encoding @MetaMmodern -> is there a way I can also set my encoding to be like yours, so I can test? I tried setting language to Croatian, and to Russian, but it uses CP437 in that case and I don't have any issues with encodings, even if I just do decodeUTF8 -> ok, escape characters are often not correctly shown, but it doesn't break. So how can I reproduce it to have the same situation as you? Because I am not happy with the current encoding solution I made, it works but it has its problems.

@MetaMmodern
Copy link
Author

@Martinsos hey, congrats on setting up Windows. For encoding: try running chcp 866 in cmd window. Should work. Got it from here: https://superuser.com/questions/1170656/windows-10-terminal-encoding

@Martinsos
Copy link
Member

Martinsos commented Jul 4, 2022

Thanks, I tried that @MetaMmodern ! But I can't reproduce the failure to parse, the one with \xad error.

I did a final commit, which:

  1. Removes usage of script for Windows -> for me wasp db migrate-dev now works! But it didn't ask me any questions. I know sometimes it asks, and I wonder if in that case it will not work. But I don't know how to get it to ask. Maybe you can try running wasp db migrate-dev, see how it goes?
  2. I reversed encoding solution to the most simple one, which works for me regardless of encoding. I wonder if this will still work for you, or will cause problems again.

TLDR; Can you give it one last spin? I did as much as I could on my Windows, for me it works now, but I wonder if it will work for you with your CP861 encoding (worked for me when I used chcp 861 but it might be different).

TODO for me: I have to decide what to do with encoding code. Probably kick out encoding library, and go with something simple, either just decodeUtf8Lenient, or use knob.

@MetaMmodern
Copy link
Author

@Martinsos What does this mean?
image
Do I need to have Postgres Installed to run WASP? Do I need to manually add this env variable or is it set by WASP CLI?

@Martinsos
Copy link
Member

Martinsos commented Jul 5, 2022 via email

@MetaMmodern
Copy link
Author

@Martinsos okay, I'll try it now.

@MetaMmodern
Copy link
Author

@Martinsos In Ukraine we call this "PEREMOGA", which means "a win"💪🏼
image
Migration done.

image
App running--done.

Important note for you to put into documentation: If you run docker on Windows using WSL you have to specify WSL host, not localhost as your host for DB connection.
You can get your host using this command: wsl hostname -I. Source is here: https://jkarelins.medium.com/run-postgresql-database-in-docker-container-on-wsl2-656ed3c02280

So that's it)

@Martinsos
Copy link
Member

Awesome @MetaMmodern well peremoga it is then :D, I can't believe we have Windows support working!! Thanks again, without you pushing this, we would not have it working soon for sure.
Let me keep this PR open few more days so I can do some final tweaks regarding the encodings + rebase it on newest master since some changes are coming there that we will need to adapt to, but everything important is done! Yay :)

@Martinsos
Copy link
Member

Martinsos commented Jul 7, 2022

@MetaMmodern , when you catch a moment (no hurry), could you run one last test? I now made a change in the code we use for decoding, so it is less convoluted + doesn't need external library -> in theory it should be the same, but if you can give it a try (wasp db migrate-dev) I would feel sure it works. Unfortunately I still haven't managed to replicate your situation with encoding in order to test the situation you had.

Btw is your output messy in any way? For me, on Windows, it is often not colored when it should be, plus some chars get replaced with ?, the exotic ones.

@MetaMmodern
Copy link
Author

@Martinsos anything for you my man)))

Yes, I have artifacts on output. Yes, I have question marks (?) at the beginning of the lines, just check my previous screenshots. But colouring works.

@MetaMmodern
Copy link
Author

@Martinsos
image
looks fine to me. well, except the question marks.

@Martinsos
Copy link
Member

@Martinsos image looks fine to me. well, except the question marks.

Mmmmm ok, those should be emojis but I guess that is not supported on Windows -> we can work on that in the future as a separate issue!

@MetaMmodern
Copy link
Author

MetaMmodern commented Jul 9, 2022

@Martinsos are you sure? I just tested emoji output, works fine.
image

Anyway I agree that should be a separate issue. Let's finish with this PR)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hacktoberfest This issue is perfect for HacktoberFest contributors hacktoberfest-accepted
Projects
None yet
Development

Successfully merging this pull request may close these issues.

wasp start failing on windows
2 participants