How to host your code: methods and philosophy

When reading the title you might think that the answer is pretty obvious: you just put your code repositories in GitHub and that's it.

However, when you think about what happened to PairDrop or spotizerr it becomes obvious that the answer is not so simple. On one hand you want to put your code in a place that is easy to reach and where your project will have exposure. On the other you don't want to rely on Big Tech to determine the future of your project. One false positive from an AI tool or one malicious DMCA request and all your hard work can just disappear. Unless your project has a big audience, nobody at Big Tech will listen to you and then it can take weeks or months until everything comes back to normal.

Mitigating risks

How do you mitigate this risk? The answer is self-hosting, before you draw the conclusion that such a thing is not feasible hear me out!

Everyone who is into self-hosting knows that it comes with its set of challenges.

By example your own domain will never have the exposure of GitHub. So you might think that self-hosting your code will reduce the exposure and viability of your project. Luckily such a thing as a push mirror exists! This means the following : first you commit your code on your self-hosted repository and then automatically the code gets pushed to another git repository. The mirror repositories can be hosted on any platform you want, like GitHub. This way you still have the exposure you want while your code is under your control.

Another challenge associated with self-hosting is managing security. If you don't want to take any risk you don't have to expose your self-hosted instance to the public. You just put the software locally and setup a push mirror to a provider that's publicly available, job done. Although with tools like pangolin exposing self-hosted services has become a breeze.

Maybe the last challenge is choosing the right software but that's what we are for.

Choosing the right software

Basically there are two options : gitea and forgejo. Personally, I use gitea, so this article only will include examples for that software. However due to the actions of the company behind it I would recommend to have a look at forgejo if you're starting out. Someday I will make the switch, but for now I'm holding out on that pending transition.

Build actions

Gitea provides a run agent that is very similar to GitHub actions. However there are some differences that need to be worked around.

Install docker

By default the gitea runner doesn't have Docker installed, in order to be able to do anything with Docker you need to install it yourself.

This can be done in the following way :

- name: Install Docker
   run: |
     echo "Checking docker installation"
     if command -v docker &> /dev/null; then
       echo "Docker installation found"
     else
       echo "Docker installation not found. Docker will be installed"
        curl -fsSL https://get.docker.com | sh
     fi

Update docker hub description

There is this action that enables you to automatically update descriptions on docker hub. However it requires some extra dependencies to be installed.

- name: Install npm dependencies
   run: |
     echo "Installing fetch"
     install_node=$false
     if ! command -v node &> /dev/null; then
       echo "No version of NodeJS detected"
       install_node=true
     else
       node_version=$(node -v)
       node_version=${node_version:1} # Remove 'v' at the beginning
       node_version=${node_version%\.*} # Remove trailing ".*".
       node_version=${node_version%\.*} # Remove trailing ".*".
       node_version=$(($node_version)) # Convert the NodeJS version number from a string to an integer.
       if [ $node_version -lt  24 ]; then
         echo "node version : " $node_version 
         echo $"removing outdated npm version"
         install_node=true
         apt-get update
         apt-get remove nodejs npm
         apt-get purge nodejs
         rm -rf /usr/local/bin/npm 
         rm -rf /usr/local/share/man/man1/node* 
         rm -rf /usr/local/lib/dtrace/node.d 
         rm -rf ~/.npm 
         rm -rf ~/.node-gyp 
         rm -rf /opt/local/bin/node 
         rm -rf opt/local/include/node 
         rm -rf /opt/local/lib/node_modules  
         rm -rf /usr/local/lib/node*
         rm -rf /usr/local/include/node*
         rm -rf /usr/local/bin/node*
       fi
     fi

     if $install_node; then
       NODE_MAJOR=24
       echo "Installing node ${NODE_MAJOR}"
       if test -f /etc/apt/keyrings/nodesource.gpg; then
         rm /etc/apt/keyrings/nodesource.gpg
       fi
       if test -f /etc/apt/sources.list.d/nodesource.list; then
         rm /etc/apt/sources.list.d/nodesource.list
       fi
       apt-get update
       apt-get install -y -q ca-certificates curl gnupg
       mkdir -p /etc/apt/keyrings
       curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
       echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
       apt-get update
       apt-get install -y -q nodejs
       npm install npm --global
     fi

     echo "node version : " $(node -v)
       
     package='node-fetch'
     if [ `npm list -g | grep -c $package` -eq 0 ]; then
       npm install -g $package
     fi
- name: Docker Hub Description
   uses: peter-evans/dockerhub-description@v5
   with:
     username: ${{ secrets.DOCKER_HUB_USERNAME }}
     password: ${{ secrets.DOCKER_HUB_PASSWORD }}  
     repository: ${{ repostitory }}