I am working together with pumpi on a project named shopmon (will be announced when it’s done) and he is lazy to set up a local development environment, and I was lazy to show him that. So I gave him an instance of VS Code running on Web on my personal VPS. Everything was fine until I broke somehow IPv6 networking with Docker containers (I really hate networking), so we looked for an alternative.

Stackblitz

We tried Stackblitz first, which it was really working fine for us until we wanted to collaborate and work together with Git branches on GitHub. Stackblitz is not made really for that, so we had to find an alternative.

Gitpod

So I remembered that we talked at the Barcamp (you have to visit event if you are interested into Shopware) about Gitpod. So we gave it a try and were really impressed. We can start a development environment for a GitHub repository with just a single click. It was really fast, and we were able to start coding instantly. We also had a preview environment for each branch and pull request. So we tested our changes directly in the browser by starting a Gitpod for the specific branch.

The project itself is a Vitejs project with Vue 3 and TypeScript. So not really a big project. A default Gitpod has 6 vCPUs and 12 GB of RAM. So it was more than enough for this simple project.

So lets quickly look into how our .gitpod.yml looks like:

ports:
  - name: Frontend
    port: 3000
    onOpen: open-browser
  - name: API
    port: 5789
    onOpen: ignore

vscode:
  extensions:
    - lukashass.volar
    - johnsoncodehk.vscode-typescript-vue-plugin
    - redhat.vscode-xml

First we can define ports that should be opened. We can also open them in the browser directly. We also can define extensions that should be installed. Likewise, we have to use the extension ID from the marketplace. We can also define a tasks section, but we didn’t need it for this project. It is not required to define the ports, if a port listens you get a notification that you can open it in the browser. But the list of ports is shown in the UI and can automatically open a Window if the port is listening.

The UI looks like:

Exposed ports

Complexer project

Setting up a Gitpod for a Vue project is simple. But what if you have a more complex project? For example a Shopware project with a MySQL database. For this I tried to add Gitpod to one of my Shopware Extensions on GitHub. So the tasks are basically:

  • Start a MySQL Server
  • Clone Shopware repository
  • Install Shopware
  • Install Extension

So to start a MySQL server I decided to use the official MySQL Docker image. So I created a new “task” in the .gitpod.yml. Tasks are Shell scripts that are executed in the container. They have three entry points:

  • before is executed before the workspace is started
  • init is executed after the workspace is started
  • command is executed after the workspace is started and the “init” command is finished
image:
  file: .gitpod.Dockerfile

tasks:
  - name: Shopware
    init: |
      # Move my cloned Extension repo
      EXTENSION_NAME=$(basename $PWD)
      TMP_DIR=$(mktemp -d)
      
      mv * .* "$TMP_DIR" || true

      # Run MySQL
      docker run --restart always -d --name=mysql -p 127.0.0.1:3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql:8

      # Create a new Shopware project
      composer create-project shopware/production:dev-flex . -n

      # Configure environment variables
      sed -i -e 's;DATABASE_URL=.*$;DATABASE_URL=mysql://root:[email protected]:3306/shopware;' .env
      sed -i -e "s;APP_URL=.*$;APP_URL=$(gp url 8000);" .env
      echo "TRUSTED_PROXIES=192.168.0.0/16" >> .env

      ./bin/console system:install --basic-setup --create-database --drop-database

      # Move actual repository
      mv "$TMP_DIR" "custom/plugins/$EXTENSION_NAME"      
    command: |
      # Gitpod registers the ports on first docker command
      docker ps
      
      # Wait for port open
      gp ports await 3306
      
      # Wait until MySQL is reachable
      until mysqladmin ping; do
          sleep 1
      done
      
      # Update domain url
      ./bin/console sales-channel:update:domain $(gp url 8000 | awk -F[/:] '{print $4}')
      
      # Start Webserver
      symfony server:start --port 8000 -d      

So what we do is in the init step:

  • Start a MySQL Server
  • Create a new Shopware 6 project using Symfony Flex template
  • Adjust some environment variables
    • The gp url 8000 command returns the URL of the Gitpod workspace when something listens on port 8000
    • Allow trusted proxies for Gitpod network
  • Install Shopware
  • Move our extension repository to the right folder

As you can prebuild a workspace. Which means that the workspace is started, and the init command is executed. So the next time you start a workspace, the init command is skipped, and the command part is only executed. So you have a quicker uptime for your workspace.

In the command part we do:

  • docker ps is used to register the ports of the before running MySQL server
  • gp ports await 3306 waits until the port is open
  • until mysqladmin ping; do sleep 1; done waits until the MySQL server is reachable
  • To Update the sales channel domain to our current Gitpod domain
  • Start the Symfony web server

You may ask now is Symfony CLI preinstalled? Nope, but you can install it with a custom docker file referenced with image.file like the first line in our YAML.

Installing additional software

So we can install additional software with a custom docker file. In this example we install nodejs, php, and symfony-cli. Normally a workspace uses gitpod/workspace-full, which contains a lot of preinstalled software. If something is missing you have to install it using a custom docker file. To reduce the image size we will use the base image and install only what we need.

FROM gitpod/workspace-base:latest

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

RUN sudo add-apt-repository ppa:ondrej/php -y && \
    curl -fsSL https://deb.nodesource.com/setup_16.x | sudo bash - && \
    curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.deb.sh' | sudo -E bash && \
    sudo apt-get install -y \
    php8.1-fpm php8.1-mysql php8.1-curl php8.1-gd php8.1-xml php8.1-zip php8.1-opcache php8.1-mbstring php8.1-intl php8.1-cli \
    rsync \
    symfony-cli \
    mysql-client-8.0 \
    nodejs && \
    sudo apt-get upgrade -y && \
    echo "memory_limit=512M" > php.ini && \
    echo "assert.active=0" >> php.ini && \
    echo "opcache.interned_strings_buffer=20" >> php.ini && \
    echo "zend.detect_unicode=0" >> php.ini && \
    echo "realpath_cache_ttl=3600" >> php.ini && \
    sudo cp php.ini /etc/php/8.1/cli/conf.d/99-overrides.ini && \
    sudo cp php.ini /etc/php/8.1/fpm/conf.d/99-overrides.ini && \
    rm php.ini && \
    echo "[client]" > ~/.my.cnf && \
    echo "host=127.0.0.1" >> ~/.my.cnf && \
    echo "user=root" >> ~/.my.cnf && \
    echo "password=root" >> ~/.my.cnf

Copy-Paste Templates

Shopware Extensions

If you are working also with Shopware extensions just copy my .gitpod.yml and .gitpod.Dockerfile to your project.

Shopware 6 Project

If you are using already the new Symfony Flex template. Just run composer req gitpod and you are ready to go with Gitpod.

Other IDEs

Gitpod does not only support VS Code in Web/Desktop. It supports also IDEs by JetBrains like PhpStorm, WebStorm and many more. You need to install to local JetBrains Product the Gitpod extension or just install Jetbrains Gateway.

Jetbrains Gateway

So you can specify like in VS Code your wanted Jetbrains Plugins. If your project is very big, you can also enable prebuild for the JetBrains IDEs. It will prebuild the IDE index in the Gitpod prebuild step. So you have a faster startup time for your IDE and don’t have to wait for the indexing.

You can use also already Fleet by launching it by own like:

curl -LSs "https://download.jetbrains.com/product?code=FLL&release.type=eap&platform=linux_x64" --output /tmp/fleet && chmod +x /tmp/fleet
/tmp/fleet launch workspace -- --auth=accept-everyone --publish --enableSmartMode "--projectDir=${GITPOD_REPO_ROOT}"

If you like more terminal editors like neovim, you can also connect straight to the SSH server of Gitpod. You can find the credentials in the Gitpod settings.

Customizing

You can customize for you as user the installation with a dotfiles repository. This repository will be cloned to the home directory of the user. So you can customize the terminal, the IDE and many more

Dotfiles

You can add also to your local .ssh/config the following:

Host *.gitpod.io
    ForwardAgent yes

So when you connect to Gitpod using VS Code on your local machine, you can use your local SSH keys to connect to external services. The credentials to push to your repository is configured by default

Pricing

Gitpod offers a free plan for their SaaS offering with 50 hours/month. If you need more hours you can upgrade to a paid plan. But you can also host your own Gitpod installation. You can find the installation guide here. If you are actively want to use it for your open-source projects you can ask also for a free open-source plan.

Conclusion

Gitpod works even very well in an ICE train with bad internet. I love to use it for small changes in my projects or other open-source projects. It works very good with VS-Code in the browser or as a Desktop Client. JetBrains IDE feels like lagging for my taste. But they are working on adding work class support to get machines with more resources. Also, I guess it’s more an issue of the IDE from JetBrains than Gitpod as JetBrains new IDE Fleet feels much more responsive.

I will get next days access to the new work classes and will test PhpStorm with it to see if its performances better. It has the opportunity for me to replace development locally, so I can finally swap between devices and continue my work. The device can be also a Raspberry Pi / Chromebook / iPad with Keyboard.

If you want to test Shopware with an extension in Gitpod, you can use my FroshTools

I hope you like this article, and you will give Gitpod a try. If you have any questions, feel free to ask them in the comments.