/ docs / guides-and-tutorials / template.md
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  ![Istantanea_2024-06-17_21-35-26 png](https://github.com/ivan-hc/AM/assets/88724353/6e11aeff-9a70-44f7-bd73-1324b545704e)
 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  ------------------------------------------------------------------------