├── Dockerfile ├── example-pipelines ├── frw.yaml └── frw-sftp.yaml ├── action.yml ├── entrypoint.sh └── README.md /Dockerfile: -------------------------------------------------------------------------------- 1 | # Container image that runs your code 2 | FROM alpine:3.17 3 | 4 | RUN apk add --no-cache git jq curl 5 | 6 | # Use icecon instead. For some reason rcon-cli doesn't send a valid command 7 | #ARG RCON_VERSION="0.10.2" 8 | # 9 | #RUN curl -LO https://github.com/gorcon/rcon-cli/releases/download/v${RCON_VERSION}/rcon-${RCON_VERSION}-amd64_linux.tar.gz && \ 10 | # tar -xvf rcon-${RCON_VERSION}-amd64_linux.tar.gz && \ 11 | # mv rcon-${RCON_VERSION}-amd64_linux/rcon /usr/local/bin/ && \ 12 | # rm -rf rcon-${RCON_VERSION}-amd64_linux* 13 | 14 | ARG ICECON_VERSION="v1.0.0" 15 | 16 | RUN wget https://github.com/icedream/icecon/releases/download/${ICECON_VERSION}/icecon_linux_i386 && \ 17 | mv icecon_linux_i386 /usr/local/bin/icecon && \ 18 | chmod +x /usr/local/bin/icecon 19 | 20 | # Copies your code file from your action repository to the filesystem path `/` of the container 21 | COPY entrypoint.sh /entrypoint.sh 22 | 23 | # Code file to execute when the docker container starts up (`entrypoint.sh`) 24 | ENTRYPOINT ["/entrypoint.sh"] 25 | -------------------------------------------------------------------------------- /example-pipelines/frw.yaml: -------------------------------------------------------------------------------- 1 | name: "Sync and Restart Resources" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | sync-restart: 8 | runs-on: "ubuntu-latest" 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | with: 13 | fetch-depth: 0 14 | - name: "Pull remote changes" 15 | uses: appleboy/ssh-action@v0.1.7 16 | env: 17 | REPO_PATH: "C:/ServerResources" 18 | with: 19 | host: ${{ secrets.SSH_HOST }} 20 | username: Administrator 21 | #key: ${{ secrets.SSH_KEY }} 22 | password: ${{ secrets.SSH_PASSWORD }} 23 | port: 22 24 | script: | 25 | cd ${{ env.REPO_PATH }} && git pull 26 | - name: "Restart resources / server" 27 | uses: illeniumstudios/fivem-resource-watcher@main 28 | env: 29 | GITHUB_EVENT_BEFORE: ${{ github.event.before }} 30 | with: 31 | serverIP: ${{ secrets.SSH_HOST }} 32 | serverPort: 30120 33 | rconPassword: ${{ secrets.RCON_PASSWORD }} 34 | restartIndividualResources: true 35 | resourcesToIgnore: "" 36 | restartServerWhen0Players: false 37 | -------------------------------------------------------------------------------- /example-pipelines/frw-sftp.yaml: -------------------------------------------------------------------------------- 1 | name: "Sync and Restart Resources" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | sync-restart: 8 | runs-on: "ubuntu-latest" 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | with: 13 | fetch-depth: 0 14 | - name: "Pull remote changes" 15 | uses: appleboy/ssh-action@v0.1.7 16 | env: 17 | REPO_PATH: "C:/ServerResources" 18 | with: 19 | host: ${{ secrets.SSH_HOST }} 20 | username: Administrator 21 | #key: ${{ secrets.SSH_KEY }} 22 | password: ${{ secrets.SSH_PASSWORD }} 23 | port: 22 24 | script: | 25 | cd ${{ env.REPO_PATH }} && git pull 26 | - name: "Restart resources / server" 27 | uses: illeniumstudios/fivem-resource-watcher@main 28 | env: 29 | GITHUB_EVENT_BEFORE: ${{ github.event.before }} 30 | with: 31 | serverIP: ${{ secrets.SSH_HOST }} 32 | serverPort: 30120 33 | rconPassword: ${{ secrets.RCON_PASSWORD }} 34 | restartIndividualResources: true 35 | resourcesToIgnore: "" 36 | restartServerWhen0Players: false 37 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: "FiveM Resource Watcher" 2 | description: "Watches for changed resources and restarts them" 3 | inputs: 4 | restartIndividualResources: 5 | description: "Restart resources individually or restart the whole server" 6 | required: false 7 | default: true 8 | type: boolean 9 | serverIP: 10 | description: "IP of the FiveM server" 11 | required: true 12 | default: "" 13 | serverPort: 14 | description: "Port of the FiveM server" 15 | required: false 16 | default: 30120 17 | rconPassword: 18 | description: "Password that you have set for rcon" 19 | required: true 20 | default: "" 21 | resourcesFolder: 22 | description: "Resources folder name" 23 | required: false 24 | default: "resources" 25 | restartServerWhen0Players: 26 | description: "Restart the server instead when there are no players on the server. (Takes priority over restartIndividualReesources)" 27 | required: false 28 | default: false 29 | type: boolean 30 | resourcesToIgnore: 31 | description: "List of resources that you want to ignore separated by spaces and not restart when changes are made to them" 32 | required: false 33 | default: "" 34 | type: string 35 | 36 | runs: 37 | using: "docker" 38 | image: "Dockerfile" 39 | args: 40 | - ${{ inputs.restartIndividualResources }} 41 | - ${{ inputs.serverIP }} 42 | - ${{ inputs.serverPort }} 43 | - ${{ inputs.rconPassword }} 44 | - ${{ inputs.resourcesFolder }} 45 | - ${{ inputs.restartServerWhen0Players }} 46 | - ${{ inputs.resourcesToIgnore }} 47 | -------------------------------------------------------------------------------- /entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -l 2 | 3 | beginswith() { case $2 in "$1"*) true ;; *) false ;; esac } 4 | 5 | exists_in_array() { 6 | local element="$1" 7 | local array_str="$2" 8 | for i in $array_str; do 9 | if [ "$i" = "$element" ]; then 10 | return 0 11 | fi 12 | done 13 | return 1 14 | } 15 | 16 | append_if_not_exists() { 17 | local element="$1" 18 | local array_str="$2" 19 | if exists_in_array "$element" "$array_str"; then 20 | echo "$array_str" 21 | else 22 | echo "$array_str $element" 23 | fi 24 | } 25 | 26 | icecon_command() { 27 | icecon --command "$1" "${SERVER_IP}:${SERVER_PORT}" "${RCON_PASSWORD}" 28 | } 29 | 30 | get_player_count() { 31 | response=$(curl -s "${SERVER_IP}:${SERVER_PORT}/players.json") 32 | player_count=$(echo "$response" | jq 'length') 33 | echo "$player_count" 34 | } 35 | 36 | RESTART_INDIVIDUAL_RESOURCES=$1 37 | SERVER_IP=$2 38 | SERVER_PORT=$3 39 | RCON_PASSWORD=$4 40 | RESOURCES_FOLDER=$5 41 | RESTART_SERVER_WHEN_0_PLAYERS=$6 42 | IGNORED_RESOURCES=$7 43 | 44 | git config --global --add safe.directory /github/workspace 45 | 46 | if [ ${GITHUB_BASE_REF} ]; then 47 | # Pull Request 48 | git fetch origin ${GITHUB_BASE_REF} --depth=1 49 | export DIFF=$(git diff --name-only origin/${GITHUB_BASE_REF} ${GITHUB_SHA}) 50 | echo "Diff between origin/${GITHUB_BASE_REF} and ${GITHUB_SHA}" 51 | else 52 | # Push 53 | git fetch origin ${GITHUB_EVENT_BEFORE} --depth=1 54 | export DIFF=$(git diff --name-status ${GITHUB_EVENT_BEFORE} ${GITHUB_SHA}) 55 | echo "Diff between ${GITHUB_EVENT_BEFORE} and ${GITHUB_SHA}" 56 | fi 57 | 58 | resources_to_restart= 59 | 60 | IFS=$'\n' 61 | for changed in $DIFF; do 62 | changed=${changed#??} 63 | if beginswith "${RESOURCES_FOLDER}" "${changed}"; then 64 | filtered=${changed##*]/} # Remove subfolders 65 | filtered=${filtered%%/*} # Remove filename and get the folder which corresponds to the resource name 66 | resources_to_restart="$(append_if_not_exists "$filtered" "$resources_to_restart")" 67 | fi 68 | done 69 | unset IFS 70 | 71 | if [ -z "$resources_to_restart" ]; then 72 | echo "Nothing to restart" 73 | else 74 | player_count=$(get_player_count) 75 | if [ "$RESTART_SERVER_WHEN_0_PLAYERS" = true ] && [ "$player_count" -eq 0 ]; then 76 | echo "Will restart the whole server due to 0 players" 77 | icecon_command "quit" 78 | elif [ "$RESTART_INDIVIDUAL_RESOURCES" = true ]; then 79 | echo "Will restart individual resources" 80 | for resource in $resources_to_restart; do 81 | if exists_in_array "${resource}" "${IGNORED_RESOURCES}"; then 82 | echo "Ignoring restart of the resource ${resource}" 83 | else 84 | echo "Restarting ${resource}" 85 | icecon_command "ensure ${resource}" 86 | fi 87 | done 88 | else 89 | echo "Will restart the whole server" 90 | icecon_command "quit" 91 | fi 92 | fi 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fivem-resource-watcher 2 | 3 | fivem-resource-watcher GitHub Action allows you to restart resources automatically when changes are pushed. 4 | 5 | ## Features 6 | 7 | - Detects change detection 8 | - Restart only resources that have been changed 9 | - Restart the whole server on changes 10 | - Resource filters 11 | - Works both on Linux and Windows 12 | 13 | ## Benefits 14 | This allows you, as a server owner to have a Git Managed workflow for your server where you don't need to provide access to the VPS / Dedicated server or txAdmin console to your developers. They just push the changes to the git repository and they all get pulled and deployed automatically 15 | 16 | ## Inputs 17 | 18 | | Input | Description | Required | Default | 19 | | ------------ | ------------ | ------------ | ------------ | 20 | | restartIndividualResources | Restart resources individually or restart the whole server | false | true | 21 | | serverIP | IP of the FiveM server | true | | 22 | | serverPort | Port of the FiveM server | false | 30120 | 23 | | resourcesFolder | Resources folder name | false | resources | 24 | | resourcesToIgnore | List of resources that you want to ignore separated by spaces and not restart when changes are made to them | false | | 25 | | restartServerWhen0Players | Restart the server instead when there are no players on the server. (Takes priority over `restartIndividualReesources`) | false | false | 26 | 27 | ## How to set up 28 | 29 | Video Tutorial / Showcase: https://youtu.be/I_FqjKvcjxY 30 | 31 | ### Explanation 32 | This action alone only does part of the automation. In order to actually make the restarts useful, we need to pull the remote changes first on the Server so that the changes are live after the restart. This doc will go through all the changes that you need to make in order to have a fully working pipeline. Your deployment workflow will look like the following after the pipeline is set up: 33 | 34 | - You make the changes in your local clone of the repository 35 | - You push the changes to the repository 36 | - The pipeline will pull the changes that you just pushed, into your server automatically 37 | - The pipeline will only restart the resources that you made changes to automatically, or it will restart the whole server depending on what you have configured 38 | 39 | **Caution:** *The pipeline utilizes a protocol known as SSH to connect to your server and pull the changes. You should be aware of the risks involved if the SSH password leaks / is not too strong. Make sure to set a secure password for your user, or use SSH Keys instead.* 40 | 41 | ### Enabling RCON 42 | 43 | The first thing that you need to do is enable RCON on your FiveM server. 44 | 45 | - To do this, simply add the following to your `server.cfg` file: 46 | 47 | ```haproxy 48 | rcon_password "somesecurepassword" 49 | ``` 50 | - then **change `somesecurepassword` to a secure passphrase.** 51 | - Once done, restart your server 52 | 53 | ### Enabling SSH (Only for Windows) 54 | 55 | This step is necessary only if you are using Windows. Linux users already have SSH enabled so this is not applicable to them. 56 | 57 | If you are still reading this part then then you are using Windows and you need to install OpenSSH server on your VPS / Dedicated Server. To do that, follow these instructions: 58 | 59 | - Connect / Login to your VPS / Dedicated server using RDP 60 | - Open your browser and go to this link to download Win32-OpenSSH: https://github.com/PowerShell/Win32-OpenSSH/releases/download/v9.1.0.0p1-Beta/OpenSSH-Win64-v9.1.0.0.msi 61 | - Install it by running the downloaded file 62 | - Allow SSH connections through your firewall 63 | - To do this, open terminal / powershell as administrator, type the following and press enter: 64 | - `netsh advfirewall firewall add rule name="Open SSH Port 22" dir=in action=allow protocol=TCP localport=22 remoteip=any` 65 | - Once done, you can test it out by opening a terminal / powershell on your local PC and type the following: 66 | 67 | ```bash 68 | ssh @ 69 | ``` 70 | 71 | - Replace `` with the username that you use to login to your RDP 72 | - Replace `` with the IP of the VPS that you use to connect to your RDP 73 | - Type in your password for the user when asked 74 | 75 | - If everything was done correctly, you should see something like this on the terminal: 76 | 77 | ```powershell 78 | Microsoft Windows [Version 10.0.20348.169] 79 | (c) Microsoft Corporation. All rights reserved. 80 | 81 | administrator@WIN-17A7N1QL3J3 C:\Users\Administrator> 82 | ``` 83 | - Type `exit` to disconnect from the SSH session 84 | 85 | 86 | ### Configuring secrets (VPS / Dedicated Server Only) 87 | 88 | Now that everything is configured on the FiveM server as well as VPS / Dedicated Server, we need to setup some secrets in your GitHub repository that you're using for your server resources. 89 | 90 | - Go to your repository, for example: https://github.com/iLLeniumTest/ServerResources 91 | - Click on `Settings` from the top bar 92 | - Click on `Secrets and variables` on the left navigation menu to expand it 93 | - Then click on `Actions` to start setting up secrets 94 | 95 | Following are the secrets that you need to configure in that section: 96 | 97 | | Secret Name | Description | 98 | | ------------ | ------------ | 99 | | SSH_HOST | IP of your server which you use to login to your server (RDP or SSH) | 100 | | SSH_PASSWORD | Password for your user that you use to login to your server (RDP or SSH) (Not required when using SSH_KEY) | 101 | | SSH_KEY | Set this in case you are using Key based authentication. (Not required when using SSH_PASSWORD) | 102 | | RCON_PASSWORD | Password that you have set in your server.cfg using `rcon_password` | 103 | 104 | - For every secret mentioned in the table above, click on `New Repository Secret` and add it 105 | - The `Name` field should have exactly the name of the secret from the table, for example `SSH_HOST`. 106 | - The `Secret` field should have the value for the secret 107 | - Make sure that you have set all of the secrets correctly before proceeding, or the pipeline will not work 108 | 109 | **Note**: `SSH_PASSWORD` and `SSH_KEY` secrets are mutually exclusive, you must either set 1 or the other. There's no need to set them both. 110 | 111 | ### Configuring Pipeline (VPS / Dedicated Server only) 112 | 113 | Now that we have configured the secrets as well, all that's left is to add the pipeline to your repository and change some of the parameters. 114 | 115 | - Start by opening up the repository in Visual Studio Code or in your browser 116 | - Create a new folder called `.github` in the root of your repository 117 | - Create a subfolder called `workflows` in the `.github` folder that you just created 118 | - Create a new file called `sync.yaml` in `.github/workflows` directory and paste the following contents into the file and save it: 119 | 120 | ```yaml 121 | name: "Sync and Restart Resources" 122 | on: 123 | push: 124 | branches: 125 | - main 126 | jobs: 127 | sync-restart: 128 | runs-on: "ubuntu-latest" 129 | steps: 130 | - name: Checkout 131 | uses: actions/checkout@v4 132 | with: 133 | fetch-depth: 0 134 | - name: "Pull remote changes" 135 | uses: appleboy/ssh-action@v0.1.7 136 | env: 137 | REPO_PATH: "C:/ServerResources" 138 | with: 139 | host: ${{ secrets.SSH_HOST }} 140 | username: Administrator 141 | #key: ${{ secrets.SSH_KEY }} 142 | password: ${{ secrets.SSH_PASSWORD }} 143 | port: 22 144 | script: | 145 | cd ${{ env.REPO_PATH }} && git pull 146 | - name: "Restart resources / server" 147 | uses: illeniumstudios/fivem-resource-watcher@main 148 | env: 149 | GITHUB_EVENT_BEFORE: ${{ github.event.before }} 150 | with: 151 | serverIP: ${{ secrets.SSH_HOST }} 152 | serverPort: 30120 153 | rconPassword: ${{ secrets.RCON_PASSWORD }} 154 | restartIndividualResources: false 155 | ``` 156 | 157 | There's a couple of things that you need to change before pushing this file: 158 | 159 | 1. On `Line 17` of the file, you need to set the folder path where you have cloned your repository on your remote VPS / Dedicated server. For example, if you are on Windows, it can be something like `C:/FiveM/MyRPServerResources`, or on Linux, it can be like `/home/username/FiveM/MyRPServerResources`. 160 | 2. On `Line 20`, set the username that you use for logging into your VPS / Dedicated Server via RDP 161 | 2. Comment out `Line 22` and uncomment `Line 21` if you're using an SSH key instead of a password for logging in. 162 | 3. On `Line 23`, set your SSH port if you have changed it explicitly. No need to do anything if you are using defaults. 163 | 4. Set your FiveM server port on `Line 32` if it is other than 30120 164 | 5. Change `restartIndividualResources` to `true` on `Line 34` if you want to restart the whole server after making changes. By default it is set to `false` which only restarts the individual resources that you have made changes to. 165 | 166 | After making the above changes, save the file and push it to the repository. 167 | 168 | And you're done. Now you should have a working pipeline that will automatically pull the changes on your remote server (VPS / Dedicated Server) if you push anything and will also restart the resources automatically for you. 169 | --------------------------------------------------------------------------------