template.md
1 # TEMPLATES: Create and test your own installation scripts 2 This is a step-by-step guide on how to create, understand and test installation scripts in this database. You can use the table of contents below to navigate between the sections easily. 3 4 **Are you ready? Let's get started!** 5 6 ------------------------------------------------------------------------ 7 ## Templates index 8 ------------------------------------------------------------------------ 9 [Create your installation script](#create-your-installation-script) 10 - [Option Zero: "**AppImages**"](#option-zero-appimages) 11 - [If the AppImage is hosted on github or codeberg](#if-the-appimage-is-hosted-on-github-or-codeberg) 12 - [If the AppImage is hosted sourceforge](#if-the-appimage-is-hosted-sourceforge) 13 - [If the AppImage is hosted on other sistes](#if-the-appimage-is-hosted-on-other-sistes) 14 - [Option One: "**build AppImages on-the-fly**"](#option-one-build-appimages-on-the-fly) 15 - [Option Two: "**Archives and other programs**"](#option-two-archives-and-other-programs) 16 - [Option Three: "**Firefox profiles**"](#option-three-firefox-profiles) 17 18 [How an installation script works](#how-an-installation-script-works) 19 20 [How to test an installation script](#how-to-test-an-installation-script) 21 22 [How to submit a Pull Request](#how-to-submit-a-pull-request) 23 24 ------------------------------------------------------------------------ 25 ## Create your installation script 26 Option `-t` or `template` allows you to create an "AM" compatible installation script using a "[templates](https://github.com/ivan-hc/AM/tree/main/templates)" that can be used by both "AM" and "AppMan". In fact, all AppMan does is take the installation scripts from this database and patch them to make them compatible with a rootless installation. 27 28 The syntax to follow is this 29 ``` 30 am -t $PROGRAM 31 ``` 32 or 33 ``` 34 appman -t $PROGRAM 35 ``` 36 and this is the screen that will appear: 37 38  39 40 Each option corresponds to a different type of application or helper to target with an installation script: 41 42 0. For **AppImages**, [press "0" (zero)](#option-zero-appimages) 43 1. To **create an AppImage on the fly** using [appimagetool](https://github.com/AppImage/AppImageKit) and [pkg2appimage](https://github.com/AppImage/pkg2appimage), [press "1" (one)](#option-one-build-appimages-on-the-fly) 44 2. For a **portable program** (archive, binary, script...), [press "2" (two)](#option-two-archives-and-other-programs) 45 3. For a **custom Firefox profile**, [press "3" (three)](#option-three-firefox-profiles) 46 47 **The next four paragraphs will explain in detail how to use the options [0](#option-zero-appimages), [1](#option-one-build-appimages-on-the-fly), [2](#option-two-archives-and-other-programs) and [3](#option-three-firefox-profiles).** 48 49 ------------------------------------------------------------------------ 50 51 | [Back to "Templates index"](#templates-index) | 52 | - | 53 54 ------------------------------------------------------------------------ 55 ## Option Zero: "AppImages" 56 The easiest script to create is certainly the one relating to AppImages, the "Zero" option. 57 58 Much depends on the site it is hosted on. 59 60 ### If the AppImage is hosted on github or codeberg 61 If the AppImage is hosted on github.com or codeberg.org, a default command will be added that is common with other repositories. 62 63 You can also specify whether to always download from "latest" (not recommended) or from the most recent release (the default). 64 65 Here's how to quickly create a script for an AppImage available on a github repository. 66 67 https://github.com/user-attachments/assets/1eec87ae-79b8-4637-a8b4-1c6314f88b86 68 69 Typically, this approach takes into account excluding all non-x86_64 architectures using the `grep -vi` command and including URLs starting with http and ending with "mage", i.e. `grep -i "http.*mage$"`. 70 71 It will also show a preview of the download URL that will be used. 72 73 If a repository contains multiple items, specify a keyword to include (choose 1) or exclude (choose 2). 74 75 In the following video, we will create a script for SimpleScreenRecorder, which is present in a repository where there are multiple AppImage packages. In the first attempt it will show me "Webcamoid", but adding the keyword `simplescreenrecorder` will give us the exact URL, as the keyword is "unique" for that package. 76 77 https://github.com/user-attachments/assets/e7bfed10-375f-405f-af7b-da7cd74862b8 78 79 This ease is due to the choice of the reference site: if you choose a URL that contains the word `github.com` or `codeberg.org`, the detection will be automatic. It is therefore preferable to add only the github/codeberg repository instead of main sites if you want an easy life. 80 81 Using different sites in fact starts to make things more complicated. See below. 82 83 ### If the AppImage is hosted sourceforge 84 If the AppImage is hosted on sourceforge.net, a dedicated function will try to intercept the app by browsing the APIs. You can also use services like repology.org if you want to find the version of the application, if it is not present in the download URL. 85 86 About the source, it should be enough to add something like this 87 ``` 88 https://sourceforge.net/p/$projectname 89 ``` 90 where $projectname is ne name of the referenced page on sourceforge.net. 91 92 You need also to specify a description for it. 93 94 A preview of the download URL will be shown as well. 95 96 Here is how to create an installation script for "Nootka". 97 98 https://github.com/user-attachments/assets/c1c08c93-3a57-45b9-9515-551555eca3c4 99 100 NOTE, if the URL does not always appear. In that case, you need to resort to more "drastic" methods. See below. 101 102 ### If the AppImage is hosted on other sistes 103 For AppImages published elsewhere, you will need more advanced knowledge of how to find the latest download URL using `curl` and `wget` (preferably the former), as well as knowledge of how to use `sed`, `grep`, and `tr`. Again, you may choose to use repology.org to determine the version if it is not present in the download URL. 104 105 When downloading an installation script using the command `am -d $program` you will notice in most cases a variable "`$version`", which represents the URL for downloading the application. 106 107 In the previous cases, it is easy to preset a URL for download, being generically "standard" sites. 108 109 Here is how a version variable looks for Abiword (github)... 110 ``` 111 version=$(curl -Ls https://api.github.com/repos/ivan-hc/Abiword-appimage/releases | sed 's/[()",{} ]/\n/g' | grep -oi "https.*mage$" | grep -vi "i386\|i686\|aarch64\|arm64\|armv7l" | head -1) 112 ``` 113 ...for SimpleScreenRecorder (still github)... 114 ``` 115 version=$(curl -Ls https://api.github.com/repos/ivan-hc/Database-of-pkg2appimaged-packages/releases | sed 's/[()",{} ]/\n/g' | grep -oi "https.*mage$" | grep -vi "i386\|i686\|aarch64\|arm64\|armv7l" | grep -i "simplescreenrecorder" | head -1) 116 ``` 117 ...and for Nootka (sourceforge) 118 ``` 119 version=$(curl -Ls https://sourceforge.net/p/nootka/activity/feed | grep -Eo "(http|https)://[a-zA-Z0-9./?=_%:-]*" | grep -i "appimage" | grep -v '%' | head -1) 120 ``` 121 ...namely, the three AppImages created previously. 122 123 All three have in common the following command, which downloads the package. 124 ``` 125 wget "$version" || exit 1 126 ``` 127 While **this is how a "`version`" variable looks in many sites other than the standard ones**, Inkscape for example have no `wget "$version"` reference... 128 ``` 129 version=$(wget -q https://repology.org/project/inkscape/related -O - | grep "version-newest" | head -1 | grep -Eo "([0-9]{1,}\.)+[0-9]{1,}") 130 wget "$(echo "https://inkscape.org/$(curl -Ls $(echo "https://inkscape.org/release/inkscape-$(curl -Ls https://inkscape.org/ | grep -Po '(?<=class="info")[^"]*' | grep -Eo "([0-9]{1,}\.)+[0-9]{1,}" | head -1)/gnulinux/appimage/dl/") | grep "click here" | grep -o -P '(?<=href=").*(?=">click)')")" || exit 1 131 ``` 132 ...this is "Lens" instead... 133 ``` 134 version=$(echo "https://api.k8slens.dev/binaries/Lens-$(wget -q https://repology.org/project/lens-kubernetes/versions -O - | grep "newest version" | head -1 | grep -o -P '(?<=">).*(?=</a)' | sed 's/^.*>//')-latest.x86_64.AppImage") 135 wget "$version" || exit 1 136 ``` 137 ...and this is "Ember" 138 ``` 139 version=$(wget -q https://repology.org/project/ember/versions -O - | grep -i "new.*version" | head -1 | tr '><' '\n' | grep "^[0-9]") 140 wget "$(curl -Ls https://www.worldforge.org/downloads/ | tr '"' '\n' | grep -i "^http.*appimage")" || exit 1 141 ``` 142 interventions like this are mostly manual. 143 144 It is advisable to test the commands to intercept the URL in another shell first, and then add them during the script creation process. 145 146 NOTE, if finding a download URL seems so difficult, consider instead that the rest of the script remains almost the same as all the other scripts. You will not have to tamper with anything else... except for rare exceptions (for example, if the AppImage is enclosed in a zip/tar/7z archive...). 147 148 In any case, read the content of the template, it is divided into sections that explain step by step what it does. 149 150 The templates are by @Samueru-sama 151 152 ------------------------------------------------------------------------ 153 154 | [Back to "Templates index"](#templates-index) | 155 | - | 156 157 ------------------------------------------------------------------------ 158 ## Option One: "build AppImages on-the-fly" 159 This was one of the very first approaches used to create this project. Before I started building AppImage packages myself, they were first compiled just like using any AUR-helper. 160 161 From version 7.1, the installation script for the AppImages is used, with the only difference that it points only to the version, while a second script will be downloaded, published separately, at [github.com/ivan-hc/AM/tree/main/appimage-bulder-scripts](https://github.com/ivan-hc/AM/tree/main/appimage-bulder-scripts), which will have the task of assembling the AppImage in the directory on the fly "tmp", during the installation process. When the second script has created the .AppImage file, the first script will continue the installation treating the created AppImage as a ready-made one downloaded from the internet. 162 163 In this video we see how "calibre" is installed (note that a "calibre.sh" file is downloaded during the process): 164 165 https://github.com/ivan-hc/AM/assets/88724353/e439bd09-5ec6-4794-8b00-59735039caea 166 167 In this video, I run the aforementioned "calibre.sh" script in a separate directory, in a completely standalone way: 168 169 https://github.com/ivan-hc/AM/assets/88724353/45844573-cecf-4107-b1d4-7e8fe3984eb1 170 171 Two different operations (assembly and installation) require two different scripts. 172 173 Fun fact, up until version 7, this option included a unique template that installed and assembled the AppImage on the fly (see [this video](https://github.com/ivan-hc/AM/assets/88724353/6ae38787-e0e5-4b63-b020-c89c1e975ddd)). This method has been replaced as it was too pretentious for a process, assembly, which may instead require many more steps, too many to be included in both an installation script and an update script (AM-updater). 174 175 ------------------------------------------------------------------------ 176 177 | [Back to "Templates index"](#templates-index) | 178 | - | 179 180 ------------------------------------------------------------------------ 181 ## Option Two: "Archives and other programs" 182 Option two is very similar to option zero, the one for AppImages, with very few fundamental differences: 183 - there is no reference to "`mage$`", so you will have to specify as a key word the extension of the file or archive to download, even if on github.com and codeberg.org 184 - you can choose to add an icon and customize a .desktop file if it is a graphical application 185 186 That said, we can even create a script for AppImage, but customizing the launcher and the icon without having to rely on the "standard" operations for which the template used in the Zero option is designed. As in this video, I'll use OBS Studio AppImage (even if this is not the use for which this template is intended) 187 188 https://github.com/ivan-hc/AM/assets/88724353/ce46e2f2-c251-4520-b41f-c511d4ce6c7d 189 190 As mentioned in parenthesis, the template is designed for many other uses, such as interception and extraction of portable archives in ZIP, TAR and 7z format, but also scripts and static binary files. 191 192 In addition, if you have chosen to add a launcher, you can always press enter and: 193 - the icon will be downloaded from the catalog [portable-linux-apps.github.io](https://portable-linux-apps.github.io/) 194 - the .deskto file will be minimal, without even names 195 196 And speaking of the .desktop file, you can replace it with the official one, copying its content... but be careful to replace the entries `Exec=` and `Icon=` with the following 197 ``` 198 Exec=$APP 199 Icon=/opt/$APP/icons/$APP 200 ``` 201 for example, if you have 202 ``` 203 [Desktop Entry] 204 Name=Friday Night Funkin' 205 Exec=PsychEngine 206 Terminal=false 207 Type=Application 208 Icon=io.github.shadowmario.fnf-psychengine.png 209 Comment=Engine originally used on Mind Games mod. 210 Categories=Game; 211 ``` 212 you must change it as 213 ``` 214 [Desktop Entry] 215 Name=Friday Night Funkin' 216 Exec=$APP 217 Terminal=false 218 Type=Application 219 Icon=/opt/$APP/icons/$APP 220 Comment=Engine originally used on Mind Games mod. 221 Categories=Game; 222 ``` 223 and most importantly, pay attention to the name of the binary file. In the example above we have `Exec=PsychEngine`, this means that the binary is not `$APP`, so you have to change the `chmod` (to made the file executable) and `ln` (to symlink the file in $PATH) references by adding the exact name of the binary, so the two references of `chmod` 224 ``` 225 chmod a+x ./$APP 226 ``` 227 must be 228 ``` 229 chmod a+x ./PsychEngine 230 ``` 231 and the `ln` reference must change from 232 ``` 233 ln -s "/opt/$APP/$APP" "/usr/local/bin/$APP" 234 ``` 235 to 236 ``` 237 ln -s "/opt/$APP/PsychEngine" "/usr/local/bin/$APP" 238 ``` 239 or you will get errors. 240 241 Same if the referenced binary is into a subdirectory. Sometime it can be into a "`bin`" directory or another path of the extracted archive. Well, in that case, you neeed to change the above like this 242 ``` 243 chmod a+x ./bin/PsychEngine 244 ``` 245 and 246 ``` 247 ln -s "/opt/$APP/bin/PsychEngine" "/usr/local/bin/$APP" 248 ``` 249 in short, if you are lucky and the binary has exactly the same name as `$APP` you will not have to change anything. Instead, you will have to manually intervene on the script, as I just described. 250 251 Note that the download rules are the same as for AppImages in the "Zero" option. If you know the content of the extracted archive, you will have no problem modifying the script manually. However, it is up to you to find the download URL. 252 253 In the case of archives, the sites github.com and codeberg.org are pre-set. Other sites must be set manually. So have the basic knowledge of SHELL described at [If the AppImage is hosted on other sistes](#if-the-appimage-is-hosted-on-other-sistes). 254 255 If in doubt, download the pre-existing installation scripts and analyze their contents to get an idea of how they work. 256 257 ------------------------------------------------------------------------ 258 259 | [Back to "Templates index"](#templates-index) | 260 | - | 261 262 ------------------------------------------------------------------------ 263 ## Option Three: "Firefox profiles" 264 Option 3 creates a launcher that opens Firefox in a custom profile and on a specific page, such as in a WebApp. I created this option to counterbalance the amount of Electron/Chrome-based applications (and because I'm a firm Firefox's supporter). 265 266 It is enough to give an AppName, a URL to the icon (a correct one, not like the one suggested in the video, down below) and a category. 267 268 Here is an example for "ProtonMail" 269 270 https://github.com/user-attachments/assets/120012de-777f-4cd0-8324-f761d8d4947a 271 272 In practice, all we need to do is create a custom Firefox profile that can be launched from a .desktop file, which is all we are really creating in this process. 273 274 **These "objects" are not even classified as applications**, and **therefore are not included in the application count when you run `am -l`**. 275 276 As suggested in the video, it is preferable to have these custom profiles for yourself. 277 278 Their advantage is to keep the data of the reference site well separated from the system profile used in Firefox. 279 280 ------------------------------------------------------------------------ 281 282 | [Back to "Templates index"](#templates-index) | 283 | - | 284 285 ------------------------------------------------------------------------ 286 ## How an installation script works 287 The structure of an installation script is designed for a system-wide installation (as root), since it is intended to be hosted in this database. But every path indicated within it is written so that "`appman -i`" or "`am -i --user`" can patch the essential parts, to hijack the installation at a local level and without root privileges. 288 289 This is a step-by-step focus on how an installation script runs, and we will take `brave` (official archive) and `brave-appimage` (the unofficial AppImage), you will see that they have a similar structure: 290 1. The header, it is ment to set the `APP` and `SITE` variable 291 ``` 292 #!/bin/sh 293 294 # AM INSTALL SCRIPT VERSION 3.5 295 set -u 296 APP=brave 297 SITE="brave/brave-browser" 298 ``` 299 2. Create the working directories (where the app will be downloaded and installed) and the "`remove`" script. Thel latter is needed to quickly remove the app in case of wrong installation or other problems 300 ``` 301 # CREATE DIRECTORIES AND ADD REMOVER 302 [ -n "$APP" ] && mkdir -p "/opt/$APP/tmp" "/opt/$APP/icons" && cd "/opt/$APP/tmp" || exit 1 303 printf "#!/bin/sh\nset -e\nrm -f /usr/local/bin/$APP\nrm -R -f /opt/$APP" > ../remove 304 printf '\n%s' "rm -f /usr/local/share/applications/$APP-AM.desktop" >> ../remove 305 chmod a+x ../remove || exit 1 306 ``` 307 3. Download and prepare the app, this part sets the `version` variable we talked at [Option Zero: "**AppImages**"](#option-zero-appimages). 308 309 The structure of the first two lines are similar for both AppImages and archives. 310 ``` 311 version=$(curl -Ls https://api.github.com/repos/brave/brave-browser/releases/latest | sed 's/[()",{} ]/\n/g' | grep -oi "https.*" | grep -vi "i386\|i686\|aarch64\|arm64\|armv7l" | grep -i "https.*linux-amd64.zip$" | head -1) 312 wget "$version" || exit 1 313 ``` 314 but while archives have this to detect and extract packages that can be present, trying to extract the content at several levels... 315 ``` 316 [ -e ./*7z ] && 7z x ./*7z && rm -f ./*7z 317 [ -e ./*tar.* ] && tar fx ./*tar.* && rm -f ./*tar.* 318 [ -e ./*zip ] && unzip -qq ./*zip 1>/dev/null && rm -f ./*zip 319 cd .. 320 if [ -d ./tmp/* 2>/dev/null ]; then mv ./tmp/*/* ./; else mv ./tmp/* ./"$APP" 2>/dev/null || mv ./tmp/* ./; fi 321 ``` 322 ...AppImages have a space where you can add manually the same commands if the AppImage is into another kind of archive 323 ``` 324 # Keep this space in sync with other installation scripts 325 # Use tar fx ./*tar* here for example in this line in case a compressed file is downloaded. 326 cd .. 327 mv ./tmp/*mage ./"$APP" 328 # Keep this space in sync with other installation scripts 329 ``` 330 all the rest is quite the same (all depends if the binary have a different name, see the last part of [Option Two: "**Archives and other programs**"](#option-two-archives-and-other-programs)) 331 ``` 332 rm -R -f ./tmp || exit 1 333 echo "$version" > ./version 334 chmod a+x ./"$APP" || exit 1 335 ``` 336 4. Symlink (note, if the binary is different from `$APP`, use the exact name, see the last part of [Option Two: "**Archives and other programs**"](#option-two-archives-and-other-programs)) 337 ``` 338 # LINK TO PATH 339 ln -s "/opt/$APP/$APP" "/usr/local/bin/$APP" 340 ``` 341 5. Creation of the script to update the program, also know as "AM-updater". It is a mix of the points 1 and 3, with in addition a system to detect newer versions of the app. 342 6. Launcher and icon, whe hare have a difference between how it works for AppImages... 343 ``` 344 # LAUNCHER & ICON 345 ./"$APP" --appimage-extract *.desktop 1>/dev/null && mv ./squashfs-root/*.desktop ./"$APP".desktop 346 ./"$APP" --appimage-extract .DirIcon 1>/dev/null && mv ./squashfs-root/.DirIcon ./DirIcon 347 COUNT=0 348 while [ "$COUNT" -lt 10 ]; do # Tries to get the actual icon/desktop if it is a symlink to another symlink 349 if [ -L ./"$APP".desktop ]; then 350 LINKPATH="$(readlink ./"$APP".desktop | sed 's|^\./||' 2>/dev/null)" 351 ./"$APP" --appimage-extract "$LINKPATH" 1>/dev/null && mv ./squashfs-root/"$LINKPATH" ./"$APP".desktop 352 fi 353 if [ -L ./DirIcon ]; then 354 LINKPATH="$(readlink ./DirIcon | sed 's|^\./||' 2>/dev/null)" 355 ./"$APP" --appimage-extract "$LINKPATH" 1>/dev/null && mv ./squashfs-root/"$LINKPATH" ./DirIcon 356 fi 357 [ ! -L ./"$APP".desktop ] && [ ! -L ./DirIcon ] && break 358 COUNT=$((COUNT + 1)) 359 done 360 sed -i "s#Exec=[^ ]*#Exec=$APP#g; s#Icon=.*#Icon=/opt/$APP/icons/$APP#g" ./"$APP".desktop 361 mv ./"$APP".desktop /usr/local/share/applications/"$APP"-AM.desktop && mv ./DirIcon ./icons/"$APP" 1>/dev/null 362 rm -R -f ./squashfs-root 363 ``` 364 ...and how it works with portable programs. You can add URLs to download them or commands to extract them from the package. Brave Browser have the easier system... 365 ``` 366 # ICON AND LAUNCHER 367 DESKTOP="https://raw.githubusercontent.com/srevinsaju/Brave-AppImage/master/AppDir/brave-browser.desktop" 368 wget "$DESKTOP" -O ./"$APP".desktop 2>/dev/null || exit 1 369 mv ./product*logo*128*.png ./DirIcon 370 sed -i "s#Exec=[^ ]*#Exec=$APP#g; s#Icon=.*#Icon=/opt/$APP/icons/$APP#g" ./"$APP".desktop 371 mv ./"$APP".desktop /usr/local/share/applications/"$APP"-AM.desktop && mv ./DirIcon ./icons/"$APP" 1>/dev/null 372 ``` 373 ...but other programs have something like this (here `funkin-psych`). 374 ``` 375 # ICON 376 mkdir -p icons 377 wget https://raw.githubusercontent.com/ShadowMario/FNF-PsychEngine/refs/heads/main/art/iconOG.png -O ./icons/"$APP" 2> /dev/null 378 379 # LAUNCHER 380 echo "[Desktop Entry] 381 Name=Friday Night Funkin' 382 Exec=$APP 383 Icon=/opt/$APP/icons/$APP 384 Terminal=false 385 Type=Application 386 Comment=Engine originally used on Mind Games mod. 387 Categories=Game;" > /usr/local/share/applications/"$APP"-AM.desktop 388 ``` 389 390 NOTE, all installation scripts work like this, except the ones for portable CLI apps, the ones without a GUI. There you can completely remove point 6 (create launcher and icon). 391 392 **This is all you need to know to create an installation script.** 393 394 ------------------------------------------------------------------------ 395 396 | [Back to "Templates index"](#templates-index) | 397 | - | 398 399 ------------------------------------------------------------------------ 400 ## How to test an installation script 401 To install and test your script, use the command 402 ``` 403 am -i /path/to/your-script 404 ``` 405 or rootless 406 ``` 407 am -i --user /path/to/your-script 408 appman -i /path/to/your-script 409 ``` 410 To debug the installation, add the option `--debug`, like this 411 ``` 412 am -i --debug /path/to/your-script 413 ``` 414 or rootless 415 ``` 416 am -i --user --debug /path/to/your-script 417 appman -i --debug /path/to/your-script 418 ``` 419 You can also simply drag and drop files into the terminal for your testing, see the video down below 420 421 https://github.com/user-attachments/assets/2b687305-fe73-4109-8871-131173755306 422 423 NOTE, since you are experimenting with scripts you created, I highly recommend using for a rootless installation for your tests. 424 425 ------------------------------------------------------------------------ 426 427 | [Back to "Templates index"](#templates-index) | 428 | - | 429 430 ------------------------------------------------------------------------ 431 ## How to submit a Pull Request 432 A good example of how Pull Requests should be done is given by the user @Sush-ruta https://github.com/ivan-hc/AM/pull/981 https://github.com/ivan-hc/AM/pull/1000 https://github.com/ivan-hc/AM/pull/960 https://github.com/ivan-hc/AM/pull/957 433 434 All files and directories are created and saved in your XDG_DESKTOP directory (~/Desktop), under "`am-scripts`". This is the structure of the directories, considering that the scripts we create ar for the x86_64 architecture... 435 ``` 436 ~/Desktop/am-scripts 437 | 438 |-portable-linux-apps.github.io 439 | | 440 | |-apps 441 | | 442 | |-icons 443 | 444 |-x86_64 445 | | 446 | |-$appname 447 | 448 |-list 449 ``` 450 ...where `portable-linux-apps.github.io` and `x86_64` are main directories, `apps` and `icons` are subdirectories, `$appname` is the script and `list` is the list of the applications you have created: 451 1. fork this repository 452 2. upload the `$appname` script from the `x86_64` directory to the "`programs/x86_64`" directory of the repository 453 3. drag and drop all the .md Markdown files from `portable-linux-apps.github.io/apps` to the first comment 454 4. still in the first comment, copy/paste the contents of the "`list`" file 455 456 I'll take care of the rest. 457 458 ------------------------------------------------------------------------ 459 460 | [Back to "Templates index"](#templates-index) | 461 | - | 462 463 ------------------------------------------------------------------------ 464 465 | [Back to "Guides and tutorials"](../../README.md#guides-and-tutorials) | [Back to "Main Index"](../../README.md#main-index) | ["Portable Linux Apps"](https://portable-linux-apps.github.io/) | [ "AppMan" ](https://github.com/ivan-hc/AppMan) | 466 | - | - | - | - | 467 468 ------------------------------------------------------------------------