Once launched the application presents a simple page at localhost:3000.
React and Docker (multi-stage builds)
The easiest way to build a React.JS application is with multi-stage builds. With multi-stage builds a Docker build can use one base image for packaging/unit tests and a different one that will hold the runtime of the application. This makes the final image more secure and smaller in size (as it does not contain any development/debugging tools).
In the case of React, you can use a base image that has Node and all testing utilities, while the final image has your server (e.g. nginx) with the static content and nothing else.
The example project is actually using multi-stage builds by default.
Here is the multi-stage Dockerfile:
Dockerfile
This docker build does the following:
Starts from the Node/Yarn image
Copies the dependencies inside the container
Copies the source code and creates all static files
Discards the Node.js image with all the JavaScript libraries
Starts again from the nginx image and copies static build result created before
The resulting is very small, as it contains only packaged/minified files.
Create a CI pipeline for React.js (Docker build)
Creating a CI/CD pipeline for React is very easy, because Codefresh can run any node image that you wish.
Here is the full pipeline that creates the Docker image after checking out the code.
codefresh.yml
This pipeline clones the source code, runs unit tests and finally creates a Docker image. Codefresh is automatically caching Docker layers (it uses the Docker image of a previous build as a cache for the next) and therefore builds will become much faster after the first one finishes.
Building a React.Js application without Docker
If your application is not dockerized yet, you can still create a pipeline that runs any command that you would run locally. You can also choose which Node version is used for each step of the pipeline by defining a different docker image for each step.
Here is the full pipeline that creates a production deployment of all files.
codefresh.yml
Notice that for demonstration purposes we uses node 11 for the tests, and node 8 for the packaging. Normally you should use the same version of node/Yarn for all your steps, but Codefresh pipelines are flexible on version of tools.
Even when you don’t create a Docker image, Codefresh still caches your workspace volume. This means that node_modules are downloaded only once. All subsequent builds will be much faster.
In the above demo, we used the npm run build command provided by the Create React App (CRA) for creating our production build. There are also other tools through which you can generate your prod build. Likewise, let us discuss a few of them in this section.
Brunch
Brunch is a Fast front-end web app build tool useful for efficiently creating production build.
set CHERE_INVOKING=1 &
set MSYSTEM=MINGW64 &
set MSYS2_PATH_TYPE=inherit &
set "MSYS2_PATH=D:\Users\USER\scoop\apps\msys2\current" &
set "PATH=%MSYS2_PATH%\usr\bin;%MSYS2_PATH%\mingw64\bin;%PATH%" &
"%ConEmuBaseDirShort%\conemu-msys2-64.exe" "/usr/bin/bash.exe" --login -i -new_console:p:C:"%MSYS2_PATH%\msys2.ico"
I have quite a long history trying to get “Linux-y” environment working on Windows (yes, I’m stuck on that platform), from enhanced cmd.exe to WSL to Git Bash.
I still think that Git Bash is the best middle ground for most people: super-easy to install (comes with Git for Windows), has very few issues and you can do almost anything you’d want on a Linux / macOS machine, e.g., rm -rf node_modules, single quotes, Bash scripting, etc.
The only limitation is that Git Bash comes with a fixed set of utilities — understandably as the project only maintains tools directly related to Git. So when you’re hunting for rsync, you're on your own. Same for zsh, and basically everything else.
I recently started using Mac besides my Windows machine and some unification is crucial for my sanity, so especially zsh became a priority. And while I was at it, I reconsidered my tool chain on Windows. In the end, I switched to MSYS2, installed Git for Windows into it but it also unlocked a world of other packages for me. This is the best setup I found so far so I’d like to share it.
Note on WSL: I love it in theory and think that it mightmake Windows the best development platform in the future (true Linux utilities, native Docker, etc.) but using it in practice feels quite cumbersome to me, as one simply is in a different operating system. For example, tools installed in WSL are not that easy to access from Windows, users are different, syncing system settings between machines works differently, updates are separate, etc. With MSYS2, there is just a single operating system: Windows.
MSYS2
Git for Windows builds on (is a friendly fork) of MSYS2 and the key to this whole setup is to turn it around: start from MSYS2 and install Git for Windows into it. This will allow us to then continue beyond its boundaries, with tools like rsync or make.
MSYS2 is the underlying goodness so it’s vital to understand what it is. The project has a nice (and short!) introduction on their wiki, in essence:
MSYS2 (usually upper case) consists of three relatively separate subsystems: msys2 , mingw32 and mingw64.
msys2 (sometimes called just msys) is an emulation layer — fully POSIX compatible but slow.
mingw subsystems provide native Windows binaries, with Linux calls rewritten at compile time to their Windows equivalents. For example, Git for Windows is a mingw64 binary (unlike msys Git which utilizes the compatibility layer and is therefore slow).
Each subsystem has its own shell and it’s important to be in the right one. The msys shell has a PATH starting with /usr/local/bin:/usr/bin:/bin:... while the mingw64 shell adds /mingw64/bin before it. This means that /mingw64/bin/git.exe is only available in the mingw64 shell.
MSYS2 comes with Pacman, a package manager ported from Arch Linux, and many packages installable by pacman -S <package>.
Install MSYS2 by downloading the 64bit version from https://www.msys2.org/, then check “Run MSYS2” and run this:
pacman -Syu # repeat if necessary pacman -Su
When asked, close the terminal entirely and start it again via the start menu shortcut “MSYS2 MSYS”.
You now have the basic environment installed and updated. Close the terminal.
$HOME
By default, $HOME is /home/You (C:\msys64\home\You). Let's switch it to C:\Users\You as usual so that for example your standard .gitconfig works.
The best way is to use the Windows dialog; add this (use the standard Windows format, not MSYS2 path /c/...):
HOME = C:\Users\You
I also tried other ways, e.g., updating C:\msys64\etc\profile or C:\msys64\etc\bash.bashrc, but they are less universal (depend on a specific shell, loading order, etc.).
UPDATE: for openssh (and maybe other programs) to work, also update the db_home line in C:\msys64\etc\nsswitch.conf to look like this:
I’ll recommend one controversial thing here: to only use the mingw64 shell from now on. This is in contrast with the official wiki which recommends to run Pacman in the msys shell but:
I didn’t hit any issues installing packages from the mingw shell.
I did hit issues installing mingw-w64-x86_64-git-lfs from the msys shell, because it uses git as part of its installation which is not in msys’ PATH.
Worrying about two shells, two ConEmu tasks, two PATH configurations, etc. is IMO not worth it until proven otherwise.
So in this section, we’ll only setup a ConEmu taks for mingw64. If you ever need the msys shell, use the “MSYS2 MSYS” shortcut in the Windows start menu.
My MSYS2::mingw ConEmu task looks like this:
set CHERE_INVOKING=1 & set MSYSTEM=MINGW64 & set MSYS2_PATH_TYPE=inherit & set “PATH=%ConEmuDrive%\msys64\mingw64\bin;%ConEmuDrive%\msys64\usr\bin;%PATH%” & %ConEmuBaseDirShort%\conemu-msys2–64.exe -new_console:p %ConEmuDrive%\msys64\usr\bin\bash.exe — login -i -new_console:C:”%ConEmuDrive%\msys64\msys2.ico”
It’s a copy of the default {Bash::Msys2-64} task with the MSYSTEM variable set (making it a mingw shell) and the PATH expanded to contain the full Windows path so that system-wide binaries like node, yarn or kubectl are accessible.
If you ever need to confirm which shell you’re in, run echo $MSYSTEM.
Essential utilities
Let’s install some basic utilities:
pacman -S man vim nano pacman -S openssh rsync make pacman -S zip unzip pacman -S mingw64/mingw-w64-x86_64-jq
Notice how easy it is to install things. For example, adding rsync to Windows is historically not easy at all — you end up going through various ports, forks, etc. This is much easier.
Now let’s install GfW to MSYS2. Most of the instructions come from this wiki page but I had to customize it a bit (it seems that the wiki page sets up the full SDK environment).
First, edit C:\msys64\etc\pacman.conf and add this:
Now update the installation with the new repository in place:
pacman -Syu
This will guide you through installing a newer msys2 runtime. You will have to exit the terminal entirely and run the update command again, as before. Repeat pacman -Syu until there are no more things to update.
Now install Git for Windows:
pacboy is used here. It is a small wrapper that saves some typing, for example, pacboy sync git:x is equivalent to pacman -S mingw-w64-x86_64-git (the ":x" means we want the x64 version).
$ git --version git version 2.18.0.windows.1$ git config --list --show-origin # ... verifies that your ~/.gitconfig is read
You should be able to pull from GitHub, credential helper should store your HTTPS password, all should be working as expected.
Update Windows path
To make Git and other tools like cp or rm -rf available also in cmd.exe and other shells, add this to your PATH in this order (I recommend system PATH which is loaded first):
C:\msys64\mingw64\bin C:\msys64\usr\bin
Verify:
Microsoft Windows [Version 10.0.17134.112] (c) 2018 Microsoft Corporation. All rights reserved.C:\Users\Borek>git --version git version 2.18.0.windows.1C:\Users\Borek>ls -la ...
Zsh
Finally, zsh! Let’s install it:
pacman -S zsh
ConEmu task is similar to what we created before, just with zsh.exe instead of bash.exe. This is my {MSYS2:zsh} task:
set CHERE_INVOKING=1 & set MSYSTEM=MINGW64 & set MSYS2_PATH_TYPE=inherit & set “PATH=%ConEmuDrive%\msys64\mingw64\bin;%ConEmuDrive%\msys64\usr\bin;%PATH%” & %ConEmuBaseDirShort%\conemu-msys2–64.exe -new_console:p %ConEmuDrive%\msys64\usr\bin\zsh.exe — login -i -new_console:C:”%ConEmuDrive%\msys64\msys2.ico”
This article has an example of a ConEmu task that builds on mintty but I think that conemu-msys2-64.exe is the preferred way, see here.
At this point, zsh is technically working but still needs some love.
Oh My Zsh
While omz feels a bit bloated to me (for example, enabling its git plugin registers about a million aliases), it is also easy to appreciate its value once you don’t have it. So I like to enable omz to get all the useful stuff it has in its lib folder but don’t enable any plugins or themes.
My preferred way to install omz and other things is via Antigen. As a start, add this to your .zshrc:
Actually, my ~/.zshrc just sources $HOME/GDrive/Settings/zsh/.zshrc so that it’s synced between computers. I'll probably switch to a versioned dotfiles approach soon.
# Use the path where you installed Antigen source "${funcsourcetrace[1]%/*}/antigen.zsh"# Load Oh My Zsh antigen use oh-my-zsh# Example of how to add other useful things antigen bundle zsh-users/zsh-completionsantigen apply
Some other essential things for me in .zshrc are:
# Make /c/... autocompletion work, see Alexpux/MSYS2-packages#38 zstyle ':completion:*' fake-files /: '/:c'# Convenient path navigation, e.g., `cd vp` setopt CDABLE_VARS vp="/c/Dev/VersionPress/versionpress" temp="/c/Dev/temp"# VSCode as an editor if [[ -n $SSH_CONNECTION ]]; then export EDITOR='vim' else export EDITOR='code-insiders --wait' fi
Prompt
The grand finale!
There are million zsh prompts out there but as expected, none is quite perfect. I want a couple of key things from a prompt:
It must be asynchronous so that querying Git info doesn’t block the actual work.
The second point is relatively hard to fulfill on Windows as most async prompts (incl. Pure) depend on zsh-async which depends on zsh/zpty which doesn't work in MSYS2 (until some fallback is implemented, see mafredri/zsh-async#26).
The only async prompt I found working on Windows is agkozak/agkozak-zsh-theme (awesome work!). I didn’t like some specifics about it, e.g., Git info being in the right prompt, so I have my own small fork of it (no prompt I saw thus far is truly customizable without forking; makes me sad). In my .zshrc:
What you cannot see in a static image is how the Git info is loaded in the background; it is really really cool.
Ok, that’s it, we now have a fully working environment with MSYS2, zsh, Oh My Zsh, Git for Windows and Pacman where adding new packages is as simple as pacman -S <something>. This is the best setup I found so far, if you have any tips please let me know in the comments below.
And big thanks to all the people involved in MSYS2 & Git for Windows projects, you make the life of a developer on Windows bearable!