├── README.md ├── check-prereqs.sh ├── clean-all.sh ├── init.sh ├── push-all.sh ├── update-all.sh ├── LICENSES ├── Zlib.txt └── MPL-2.0.txt ├── README.legacy.md └── shared-functions.sh /README.md: -------------------------------------------------------------------------------- 1 | **This project is no longer active** 2 | 3 | --- 4 | 5 | ## Up to date onboarding documentation 6 | 7 | The user-facing Serpent OS tooling onboarding documentation now lives [here](https://github.com/serpent-os/moss). 8 | -------------------------------------------------------------------------------- /check-prereqs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-License-Identifier: MPL-2.0 4 | # 5 | # Copyright: © 2023 Serpent OS Developers 6 | # 7 | 8 | # Initial prerequisite check for the ability to build the 9 | # Serpent OS Dlang tooling 10 | 11 | # Be helpful if the user supplies an argument 12 | if [[ -n "${1}" ]]; then 13 | cat << EOF 14 | 15 | Usage: check-prerequisites.sh 16 | 17 | Check if all prerequisites for building the Serpent OS tooling are present. 18 | 19 | EOF 20 | exit 0 21 | fi 22 | 23 | # ensure we get can source the shared functions properly 24 | ONBOARDING_DIR=$(dirname "${0}") 25 | 26 | source "${ONBOARDING_DIR}/shared-functions.sh" 27 | 28 | checkPrereqs 29 | -------------------------------------------------------------------------------- /clean-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-License-Identifier: MPL-2.0 4 | # 5 | # Copyright: © 2023 Serpent OS Developers 6 | # 7 | 8 | # Be helpful if the user supplies an argument 9 | if [[ -n "${1}" ]]; then 10 | cat << EOF 11 | 12 | Usage: clean-all.sh 13 | 14 | Run 'meson compile --clean' for all Serpent OS tool repos. 15 | 16 | NB: Please run the script from the serpent-os/ clone root directory containing 17 | the Serpent OS core git repos as cloned by 'clone-all.sh'. 18 | 19 | EOF 20 | exit 0 21 | fi 22 | 23 | # ensure we get can source the shared functions properly 24 | ONBOARDING_DIR=$(dirname "${0}") 25 | 26 | source "${ONBOARDING_DIR}/shared-functions.sh" 27 | 28 | cleanAllTools 29 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-License-Identifier: MPL-2.0 4 | # 5 | # Copyright: © 2023 Serpent OS Developers 6 | # 7 | 8 | cat << EOF > ./update.sh 9 | #!/usr/bin/env bash 10 | # 11 | # SPDX-License-Identifier: MPL-2.0 12 | # 13 | # Copyright: © 2023 Serpent OS Developers 14 | # 15 | 16 | function failMsg () 17 | { 18 | echo -e "|\n'- \${1}\\n" 19 | exit 1 20 | } 21 | 22 | if [[ -d onboarding/.git/ ]]; then 23 | git -C onboarding/ pull --rebase || failMsg 'onboarding/ repo not clean. Cannot update it. Aborting.' 24 | else 25 | git clone https://github.com/serpent-os/onboarding.git 26 | fi 27 | 28 | exec onboarding/update-all.sh 29 | EOF 30 | 31 | chmod a+x ./update.sh 32 | exec ./update.sh 33 | -------------------------------------------------------------------------------- /push-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-License-Identifier: MPL-2.0 4 | # 5 | # Copyright: © 2023 Serpent OS Developers 6 | # 7 | 8 | # Push all Serpent OS Dlang repos checked out by clone-all.sh 9 | 10 | # Be helpful if the user supplies an argument 11 | if [[ -n "${1}" ]]; then 12 | cat << EOF 13 | 14 | Usage: push-all.sh 15 | 16 | Push all existing clones of the Serpent OS tool repositories. 17 | 18 | NB: Please run the script from the serpent-os/ git clone root. 19 | This script requires commit access to the Serpent OS git repos. 20 | 21 | EOF 22 | exit 0 23 | fi 24 | 25 | # ensure we get can source the shared functions properly 26 | ONBOARDING_DIR=$(dirname "${0}") 27 | 28 | source "${ONBOARDING_DIR}/shared-functions.sh" 29 | 30 | pushAllRepos 31 | -------------------------------------------------------------------------------- /update-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-License-Identifier: MPL-2.0 4 | # 5 | # Copyright: © 2023 Serpent OS Developers 6 | # 7 | 8 | # Be helpful if the user supplies an argument 9 | if [[ -n "${1}" ]]; then 10 | cat << EOF 11 | 12 | Usage: update-all.sh 13 | 14 | Clone or pull+rebase all the Serpent OS service repositories. 15 | 16 | Upon successful clone/pull operations, check prerequisites 17 | and (re)build the serpent services if the check passes. 18 | 19 | NB: Please run the script from the serpent-os/ git clone root. 20 | 21 | EOF 22 | exit 0 23 | fi 24 | 25 | # ensure we get can source the shared functions properly 26 | ONBOARDING_DIR=$(dirname "${0}") 27 | 28 | source "${ONBOARDING_DIR}/shared-functions.sh" 29 | 30 | # fail up-front according to the principle of least astonishment 31 | checkPrereqs && updateAllRepos && time buildAllDLangTools 32 | updateUsage 33 | -------------------------------------------------------------------------------- /LICENSES/Zlib.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2020-2022 Serpent OS Developers 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source distribution. 20 | -------------------------------------------------------------------------------- /README.legacy.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Legacy Onboarding documentation and convenience scripts for cloning/building/pulling/pushing the Serpent OS DLang tooling. 4 | 5 | For a general overview of the goals of Serpent OS and how to get in touch, see our [website](https://serpentos.com). 6 | 7 | ## Onboarding 8 | 9 | Serpent OS tooling is being redesigned in [Rust](https://rustlang.org). 10 | 11 | The legacy PoC tooling was written primarily in [Dlang](https://dlang.org/). 12 | 13 | ### Prerequisites 14 | 15 | We use: 16 | 17 | - [`git`](https://git-scm.com/) to manage development. 18 | - [`cargo`](https://doc.rust-lang.org/cargo/index.html) and [`rustc`](https://doc.rust-lang.org/rustc/index.html) to build our Rust binaries. 19 | - [`rustfmt`](https://rust-lang.github.io/rustfmt/) to format our Rust code consistently. 20 | - [`meson`](https://mesonbuild.com/) (with [`dub`](https://dub.pm/) as a fallback) and [`ldc2`](https://wiki.dlang.org/LDC) to build our legacy DLang binaries. 21 | - [`dfmt`](https://github.com/dlang-community/dfmt) to format our legacy DLang code consistently. Consult the [`dfmt` README](https://github.com/dlang-community/dfmt#installation) for how to build it with LDC. Our scripts assume that `dfmt` is available in `$PATH`. 22 | - the python module `codespell` for spell checking. Install it from your distribution's package manager. 23 | - the [`go-task`](https://github.com/go-task/task/releases) Go application to maintain frequently executed job compositions without having to clutter a repository with Makefiles or the like. 24 | 25 | For convenience, we maintain a `check-prereqs.sh` script, which will check for all necessary binaries, runtime libraries and development headers and report missing prerequisites. 26 | 27 | #### Rust Toolchain installation 28 | 29 | Most Linux distributions ship recent versions of both `cargo`, `rustc` and `rustfmt`. Consult your distribution's documentation for more information on how to install the relevant packages. 30 | 31 | #### LDC Dlang Toolchain installation (DMD not supported) 32 | 33 | _The latest supported ldc version is **1.32.2**_. 34 | 35 | The currently recommended way to install the Dlang toolchain is to use the official install script: 36 | 37 | curl -fsS https://dlang.org/install.sh | bash -s ldc-1.32.2 38 | 39 | **NB:** Remember to _source_ ('activate') the appropriate environment initialisation script from your preferred shell's user config file. 40 | 41 | #### LDC Dlang Toolchain update 42 | 43 | One of our users kindly shared their experience updating an already installed LDC instance. [Read more here](https://forums.serpentos.com/d/22-how-to-test-moss/4) 44 | 45 | ### Repo structure 46 | 47 | Our Rust tooling repository is structured with multiple workspaces, so only a single repo needs to be cloned, which makes development more convenient. 48 | 49 | For our legacy DLang tooling, we use a flat repository structure where all Dlang `meson`-controlled subprojects are expected to be checked out concurrently. 50 | 51 | This forces a "lockstep" development methodology, which means that whatever is currently checked out in each subproject is what any given binary will be built against. 52 | 53 | This also implies that all subprojects will need to be kept in sync with the features that are being worked on (preferably using identical topic branch names). 54 | 55 | The only place we use "full" git submodules is in `moss-vendor`. 56 | 57 | #### Getting and building the serpent tooling (deprecated) 58 | 59 | Here, all relevant Serpent OS subprojects will be checked out under `~/repos/serpent-os/` 60 | 61 | ``` 62 | # Initial setup 63 | mkdir -pv ~/repos/serpent-os/ 64 | cd ~/repos/serpent-os/ 65 | 66 | curl https://raw.githubusercontent.com/serpent-os/onboarding/main/init.sh |bash 67 | ``` 68 | 69 | #### A note on RAM requirements 70 | 71 | A system with at least **8 GiB of RAM is recommended** and compressed swap (zram or zswap) might make the build experience smoother whilst a webbrowser is open. 72 | 73 | In addition, the onboarding build scripts will attempt to tune the amount of `boulder` parallel build jobs to fit with the available memory on the system. 74 | 75 | ### Legacy serpent DLang tooling build order 76 | 77 | To get started packaging with the legacy pre-alpha quality serpent tooling, the following binaries need to be built in the order listed below: 78 | 79 | - [`moss-container`](https://github.com/serpent-os/moss-container) (our lightweight container tool) 80 | - [`boulder`](https://github.com/serpent-os/boulder) (our system software build tool) 81 | 82 | The `./update.sh` script updates, builds and installs the serpent tooling to `/usr` in the order listed above. It also builds and installs the new Rust-based moss binary. 83 | 84 | ## Short introduction to the Serpent OS packaging workflow 85 | 86 | - Create new recipe: `boulder new https://some.uri/to-a-package-version.tar.gz` -> outputs new `stone.yml` recipe 87 | - Edit new recipe: `nano -w stone.yml` 88 | - Build new recipe: `boulder build stone.yml -p local-x86_64` -> outputs `package.stone` + metadata (`manifest.*` build manifests) in current directory 89 | - Copy new package.stone to the local binary moss repo and include it in index: `cp package.stone /var/cache/boulder/repos/local-x86_64/ && moss index /var/cache/boulder/repos/local-x86_64/` 90 | - Add local repo to repos searched by moss: `moss repo add local-x86_64 file:///var/cache/boulder/repos/local-x86_64/stone.index -p 10` 91 | - Install package from local collection: `moss install package` 92 | 93 | ### Creating a stone.yml recipe template 94 | 95 | The high level flow is that packagers start by creating a `stone.yml` build recipe using `boulder new URI-to-tarball-they-wish-to-package`. This outputs a bare bones `stone.yml` recipe that will need fleshing out with summary, description, patches, build steps etc. 96 | 97 | ### Building a stone.yml recipe into a binary package.stone 98 | 99 | To actually build a Serpent OS format .stone binary package, packagers invoke `sudo boulder build stone.yml`. This will parse the `stone.yml` build recipe and execute the various setup, build, install etc. steps specified in the recipe and discover + add relevant metadata, dependencies etc. to the finished `somepackage.stone` binary build artefact. The process will also produce a moss-readable binary format build manifest named `manifest.$arch.bin` plus a human readable `manifest.jsonc` file containing essentially the same metadata as the binary manifest, but only used for `git diff` purposes. 100 | 101 | ### Adding package.stone to a moss repository 102 | 103 | Binary moss .stone packages are kept in moss repositories, which each have a `stone.index` file containing the metadata from all the .stone packages in the repo. Thus, to be able to install a newly built package, it will need to be moved to a known moss repo, which then needs to have its `stone.index` file updated to include the metadata from the newly added .stone. 104 | 105 | Once the moss repo index has been updated, moss will be able to install the package that was just added to the moss repo. 106 | 107 | The following section details how to get started with this process. 108 | 109 | ## Initial moss setup for running a Serpent OS systemd-nspawn container 110 | 111 | To be able to actually use moss, its various databases need to be initialised inside a clean folder, which will function as a root directory for a systemd-nspawn container later on. 112 | 113 | This can be accomplished by executing: 114 | 115 | cd img-tests/ 116 | ./create-sosroot.sh 117 | 118 | which will install a suitable set of packages for use in a systemd-nspawn container. 119 | 120 | Boot a systemd-nspawn container with the installed minimal Serpent OS system: 121 | 122 | sudo systemd-nspawn --bind=/var/cache/boulder/ -D ./sosroot/ -b 123 | 124 | To stop and exit the systemd-nspawn container, issue the following command from within the container: 125 | 126 | poweroff 127 | 128 | If the container locks up or stops responding, you might be able to use `machinectl` to stop it from outside the container: 129 | 130 | sudo machinectl poweroff sosroot 131 | 132 | ### Local moss repo support 133 | 134 | Moss and Boulder now support profiles that include multiple moss repos with priorities (higher priority overrides lower priority). Things are still a bit rough around the edges, but the following instructions should get you going with packaging in a local collection and using the .stones you put there as dependencies for subsequent builds: 135 | 136 | # create /var/cache/boulder/repos/local-x86_64 137 | sudo mkdir -pv /var/cache/boulder/repos/local-x86_64 138 | # ensure your user has write access to the local moss repo 139 | sudo chown -Rc ${USER}:${USER} /var/cache/boulder/repos/local-x86_64 140 | # dowload/prepare a set of stones there (can be empty initially), 141 | # then create a moss stone.index file 142 | moss -D sosroot/ index /var/cache/boulder/repos/local-x86_64/ 143 | # add the new collection to the list of known collections to moss (highest priority so far) 144 | moss -D sosroot/ repo add local-x86_64 file:///var/cache/boulder/repos/local-x86_64/stone.index -p10 145 | # Ask moss to list the available .stones (including now the ones in the local colleciton) 146 | moss -D sosroot/ list available 147 | # newest boulder ships with a profile configuration that enables using the 148 | # local repo for dependencies, so no need to add it before building 149 | sudo boulder build stone.yml -p local-x86_64 150 | 151 | **NB**: Currently, whenever a new .stone is added to a local repo, the local repo index needs to be updated with `moss index (...)`. 152 | 153 | ## Support 154 | 155 | Please refer to the [website](https://serpentos.com) for instructions on how to get in touch with the Serpent OS developers. 156 | 157 | ## Contributing 158 | 159 | Please get in touch with the [Serpent OS developers](https://serpentos.com/team) before contributing pull requests. 160 | 161 | We're a friendly bunch and will likely welcome your contributions. 162 | 163 | ## License 164 | 165 | Serpent OS is licensed under the MPL-2.0 license. Legacy tooling is licensed under the Zlib license. 166 | 167 | -------------------------------------------------------------------------------- /LICENSES/MPL-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | Mozilla Public License Version 2.0 4 | 5 | ================================== 6 | 7 | 1. Definitions 8 | 9 | -------------- 10 | 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 11 | 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 12 | 1.3. "Contribution" means Covered Software of a particular Contributor. 13 | 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 14 | 1.5. "Incompatible With Secondary Licenses" means 15 | (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or 16 | (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 17 | 1.6. "Executable Form" means any form of the work other than Source Code Form. 18 | 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 19 | 1.8. "License" means this document. 20 | 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 21 | 1.10. "Modifications" means any of the following: 22 | (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or 23 | (b) any new file in Source Code Form that contains any Covered Software. 24 | 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 25 | 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 26 | 1.13. "Source Code Form" means the form of the work preferred for making modifications. 27 | 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 28 | 29 | 2. License Grants and Conditions 30 | 31 | -------------------------------- 32 | 2.1. Grants 33 | 34 | Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: 35 | (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and 36 | (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 37 | 2.2. Effective Date 38 | 39 | The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 40 | 2.3. Limitations on Grant Scope 41 | 42 | The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: 43 | (a) for any code that a Contributor has removed from Covered Software; or 44 | (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or 45 | (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. 46 | 47 | This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 48 | 2.4. Subsequent Licenses 49 | 50 | No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 51 | 2.5. Representation 52 | 53 | Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 54 | 2.6. Fair Use 55 | 56 | This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 57 | 2.7. Conditions 58 | 59 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 60 | 61 | 3. Responsibilities 62 | 63 | ------------------- 64 | 3.1. Distribution of Source Form 65 | 66 | All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 67 | 3.2. Distribution of Executable Form 68 | 69 | If You distribute Covered Software in Executable Form then: 70 | (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and 71 | (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 72 | 3.3. Distribution of a Larger Work 73 | 74 | You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 75 | 3.4. Notices 76 | 77 | You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 78 | 3.5. Application of Additional Terms 79 | 80 | You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 81 | 82 | 4. Inability to Comply Due to Statute or Regulation 83 | 84 | --------------------------------------------------- 85 | 86 | If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 87 | 88 | 5. Termination 89 | 90 | -------------- 91 | 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 92 | 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 93 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. 94 | 95 | ************************************************************************ 96 | 97 | 6. Disclaimer of Warranty 98 | 99 | * ------------------------- * 100 | 101 | Covered Software is provided under this License on an "as is" basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the Covered Software is free of defects, merchantable, fit for a particular purpose or non-infringing. The entire risk as to the quality and performance of the Covered Software is with You. Should any Covered Software prove defective in any respect, You (not any Contributor) assume the cost of any necessary servicing, repair, or correction. This disclaimer of warranty constitutes an essential part of this License. No use of any Covered Software is authorized under this License except under this disclaimer. 102 | 103 | ************************************************************************ 104 | 105 | ************************************************************************ 106 | 107 | 7. Limitation of Liability 108 | 109 | * -------------------------- * 110 | 111 | Under no circumstances and under no legal theory, whether tort (including negligence), contract, or otherwise, shall any Contributor, or anyone who distributes Covered Software as permitted above, be liable to You for any direct, indirect, special, incidental, or consequential damages of any character including, without limitation, damages for lost profits, loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses, even if such party shall have been informed of the possibility of such damages. This limitation of liability shall not apply to liability for death or personal injury resulting from such party's negligence to the extent applicable law prohibits such limitation. Some jurisdictions do not allow the exclusion or limitation of incidental or consequential damages, so this exclusion and limitation may not apply to You. 112 | 113 | ************************************************************************ 114 | 115 | 8. Litigation 116 | 117 | ------------- 118 | 119 | Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 120 | 121 | 9. Miscellaneous 122 | 123 | ---------------- 124 | 125 | This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 126 | 127 | 10. Versions of the License 128 | 129 | --------------------------- 130 | 10.1. New Versions 131 | 132 | Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 133 | 10.2. Effect of New Versions 134 | 135 | You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 136 | 10.3. Modified Versions 137 | 138 | If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 139 | 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses 140 | 141 | If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. 142 | -------------------------------------------------------------------------------- /shared-functions.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # SPDX-License-Identifier: MPL-2.0 4 | # 5 | # Copyright: © 2023 Serpent OS Developers 6 | # 7 | 8 | # shared-functions.sh: 9 | # Base library of functions for the scripts used to manage all prerequisite 10 | # serpent-os tooling repositories 11 | 12 | ## Environment overrides: 13 | # 14 | # Github prefix (useful for when preparing PRs from a user repo) 15 | GH_NAMESPACE="${GH_NAMESPACE:-serpent-os}" 16 | echo -e "\nUsing GH_NAMESPACE: ${GH_NAMESPACE}" 17 | # 18 | # install prefix for tooling (allow environment override) 19 | INSTALL_PREFIX="${INSTALL_PREFIX:-/usr/local}" 20 | echo -e "\nUsing INSTALL_PREFIX: ${INSTALL_PREFIX}" 21 | # 22 | ## Environment overrides END 23 | 24 | # Add escape codes for color 25 | RED='\033[0;31m' 26 | RESET='\033[0m' 27 | 28 | # Capture current run-dir 29 | RUN_DIR="${PWD}" 30 | 31 | # Download via HTTPS (negotiates faster than SSH), push via SSH 32 | SSH_PREFIX="git@github.com:${GH_NAMESPACE}" 33 | HTTPS_PREFIX="https://github.com/${GH_NAMESPACE}" 34 | 35 | # Make it easier to selectively check out branches per project 36 | declare -A CORE_REPOS 37 | CORE_REPOS['avalanche']=main 38 | #CORE_REPOS['boulder']=main 39 | #CORE_REPOS['img-tests']=main 40 | CORE_REPOS['libmoss']=main 41 | #CORE_REPOS['moss']=main 42 | #CORE_REPOS['moss-container']=main 43 | CORE_REPOS['moss-service']=main 44 | CORE_REPOS['summit']=main 45 | CORE_REPOS['vessel']=main 46 | 47 | function failMsg() 48 | { 49 | echo -e "$*" 50 | exit 1 51 | } 52 | 53 | # check that the directory given in ${1} exists and is a git repo 54 | function isGitRepo () 55 | { 56 | if [[ -d "${1}"/.git/ ]]; then 57 | return 0 # "success" 58 | else 59 | return 1 # ! "success" 60 | fi 61 | } 62 | 63 | # Should be run from within a known good git repo 64 | function checkGitStatusClean () 65 | { 66 | # we don't care about non-tracked files in git-status --short output 67 | local GIT_STATUS="$(git -C ${1} status --short |grep -v ??)" 68 | if [[ "$GIT_STATUS" == "" ]]; then 69 | return 0 70 | else 71 | failMsg "\n Git repo ${1} contains uncommitted changes.\n '- Aborting!\n" 72 | fi 73 | } 74 | 75 | 76 | PREREQ_NOT_FOUND=0 77 | # Check for all tools, libraries and headers before bailing 78 | function checkPrereqs() 79 | { 80 | # Bash associative arrays are well suited for this kind of thing 81 | declare -A bin 82 | bin['pkg-config tool']=pkg-config 83 | bin['Binutils']=ld 84 | bin['C compiler']=cc 85 | bin['CMake build tool']=cmake 86 | #bin['Rust Cargo package manager']=cargo 87 | bin['Codespell python tool']=codespell 88 | bin['Dlang code formatter']=dfmt 89 | bin['Dlang package manager']=dub 90 | bin['Fakeroot tool']=fakeroot 91 | bin['GNU Awk interpreter']=gawk 92 | bin['Git version control tool']=git 93 | bin['Go-task']=go-task 94 | bin['LDC D compiler v1.32.2']=ldc2 95 | bin['Meson build tool']=meson 96 | bin['Ninja build tool']=ninja 97 | #bin['Rust compiler']=rustc 98 | #bin['Rust code formatter']=rustfmt 99 | bin['Sudo']=sudo 100 | 101 | echo -e "\nChecking for necessary tools/binaries" 102 | # 'all keys in the bin associative array' 103 | for b in "${!bin[@]}" ; do 104 | # distributions use 'go-task', upstream uses 'task' ¯\_(ツ)_/¯ 105 | local found 106 | if [[ ${bin[$b]} =~ "go-task" ]]; then 107 | found=$(command -v task || command -v go-task) 108 | elif [[ ${bin[$b]} == "ldc2" ]]; then 109 | local candidate=$(command -v "${bin[$b]}") 110 | if [[ -n $candidate ]]; then 111 | local ldc_version=$(ldc2 --version |head -n1 |grep -o '1.32.2') 112 | if [[ "${ldc_version}" != "1.32.2" ]]; then 113 | echo -e "- ${b} (${bin[$b]}) version 1.32.2 ${RED}not found${RESET} in \$PATH." 114 | PREREQ_NOT_FOUND=1 115 | continue 116 | else 117 | found="$candidate" 118 | fi 119 | fi 120 | else 121 | found=$(command -v "${bin[$b]}") 122 | fi 123 | if [[ -z $found ]]; then 124 | echo -e "- ${b} (${bin[$b]}) ${RED}not found${RESET} in \$PATH." 125 | PREREQ_NOT_FOUND=1 126 | else 127 | echo "- found ${b} (${found})" 128 | fi 129 | unset local 130 | done 131 | 132 | echo -e "\nChecking for necessary development libraries and headers (-devel packages):" 133 | # Key is the .pc name (without extension) -- e.g. libcurl.pc -> libcurl 134 | # Value is the invocation parameters for a successful pkg-config match 135 | # FIXME: Determine and set correct minimum versions 136 | declare -A pc 137 | pc[dbus-1]='--atleast-version=1.14' 138 | #pc[libgit2]='--atleast-version=1.3.0' 139 | pc[libcurl]='--atleast-version=7.5' 140 | pc[libsodium]='--atleast-version=1.0' 141 | pc[libxxhash]='--atleast-version=0.0.1' 142 | pc[libzstd]='--atleast-version=1' 143 | pc[mount]='--atleast-version=2.37' 144 | pc[openssl]='--atleast-version=3.1' 145 | #pc[rocksdb]='--atleast-version=6.22' 146 | # upstream doesn't ship a .pc file -- it's patched in on major distros 147 | pc[lmdb]='--atleast-version=0.9' 148 | 149 | for p in ${!pc[@]}; do 150 | echo "- ${p} -devel package:" 151 | pkg-config --exists ${p} 152 | if [[ ! $? -eq 0 ]]; then 153 | echo -e " - ${p} -devel package ${RED}not found.${RESET}" 154 | PREREQ_NOT_FOUND=1 155 | else 156 | echo " - checking version requirement (${pc[$p]}):" 157 | pkg-config --print-errors ${pc[$p]} ${p} 158 | if [[ ! $? -eq 0 ]]; then 159 | echo " - ${p} -devel package installed, but ${RED}does not meet version requirement.${RESET}" 160 | PREREQ_NOT_FOUND=1 161 | else 162 | echo " - found ${p}.pc file which meets version requirement." 163 | fi 164 | fi 165 | done 166 | 167 | # For packages which typically don't have .pc files 168 | declare -A header 169 | header[glibc]='/usr/include/gnu/lib-names-64.h' 170 | header[kernel]='/usr/include/linux/elf.h' 171 | header[lmdb]='/usr/include/lmdb.h' 172 | 173 | for h in ${!header[@]}; do 174 | if [[ "$h" == "glibc" ]]; then 175 | # debian and fedora differ here 176 | ls ${header[$h]} >/dev/null 2>&1 || ls /usr/include/x86_64-linux-gnu/gnu/lib-names-64.h >/dev/null 2>&1 177 | else 178 | ls ${header[$h]} >/dev/null 2>&1 179 | fi 180 | if [[ ! $? -eq 0 ]]; then 181 | echo -e "- ${h} -devel headers (${header[$h]}) ${RED}not found.${RESET}" 182 | PREREQ_NOT_FOUND=1 183 | else 184 | echo "- found ${h} -devel headers (${header[$h]})" 185 | fi 186 | done 187 | 188 | # This is by far the slowest check, so leave it for last 189 | # - it ensures runtime access to C libraries from Dlang C bindings 190 | declare -A lib 191 | lib[curl]=libcurl.so.4 192 | #lib[rocksdb]=librocksdb.so 193 | lib[lmdb]=liblmdb.so* 194 | lib[xxhash]=libxxhash.so.0 195 | lib[zstd]=libzstd.so.1 196 | 197 | echo -e "\nChecking for the existence of non-development (runtime) libraries" 198 | for l in ${!lib[@]}; do 199 | find /usr/lib{,64} -name ${lib[$l]} 2>/dev/null |xargs stat &>/dev/null 200 | if [[ ! $? -eq 0 ]]; then 201 | echo -e "- ${l} runtime library (${lib[$l]}) ${RED}not found.${RESET}" 202 | PREREQ_NOT_FOUND=1 203 | else 204 | echo "- found ${l} runtime library (${lib[$l]})" 205 | fi 206 | done 207 | 208 | if [[ ${PREREQ_NOT_FOUND} -gt 0 ]]; then 209 | failMsg "\nPlease ensure that all necessary tools, libraries and headers are installed.\n" 210 | else 211 | echo -e "\nFound all necessary tools, libraries and headers.\n" 212 | fi 213 | } 214 | 215 | # Emit message if ${HOME}/.local/bin is not in $PATH 216 | function checkPath () 217 | { 218 | if [[ ! "${PATH}" =~ "${HOME}/.local/bin" ]]; then 219 | echo -e "\nRemember to add \${HOME}/.local/bin to \$PATH \!\n" 220 | fi 221 | } 222 | 223 | # build tool (= dir under git control) specified in ${1} 224 | # this function is assumed to be run from the directory 225 | # below the individual clones (clone root) 226 | function buildDLangTool () 227 | { 228 | # Conservatively limit memory consumption during compilation of 229 | # the drafter/ licence stuff in boulder, due to each active ldc2 230 | # instance using up to 1.6GiB resident memory worst case. 231 | local THREADS=$(nproc) 232 | # Assume that at least 2 GiB is available! 233 | if [[ "${1}" == "boulder" && ${THREADS} -gt 1 ]]; then 234 | local MEM_AVAILABLE=$(gawk '/MemAvailable/ { GiB = $2/(1024*1024); print GiB }' /proc/meminfo) 235 | local MAX_JOBS=$(echo "${MEM_AVAILABLE}" |gawk '{ print int($1/1.6) }') 236 | if [[ ${MAX_JOBS} -lt ${THREADS} ]]; then 237 | local JOBS="-j${MAX_JOBS}" 238 | echo -e "\n INFO: Restricting to ${JOBS} parallel boulder build jobs (Free RAM: ~${MEM_AVAILABLE} GiB)\n" 239 | else 240 | echo -e "\n INFO: Using ${THREADS} parallel boulder build jobs\n" 241 | fi 242 | fi 243 | 244 | isGitRepo "${1}" || \ 245 | failMsg "${1} does not appear to be a serpent tooling repo?" 246 | # Make the user deal with unclean git repos 247 | checkGitStatusClean "${1}" 248 | 249 | pushd "${1}" 250 | # We want to unconditionally (re)configure the build, if a previous 251 | # build/ dir exists. 252 | # 253 | # ${JOBS:-} is expanded to nothing if JOBS isn't set above 254 | # which implies using the number of available hardware threads 255 | if [[ -d build/ ]]; then 256 | echo -e "\nAttempting to Uninstall prior installs of ${1} ...\n" 257 | sudo ninja uninstall -C build/ 258 | fi 259 | echo -e "\nResetting ownership as a precaution ...\n" 260 | sudo chown -Rc ${USER}:${USER} * 261 | echo -e "\nConfiguring, building and installing ${1} ...\n" 262 | if [[ -f meson.build ]] 263 | then 264 | ( meson setup -Dbuildtype=debugoptimized --prefix="${INSTALL_PREFIX}" --wipe build/ || meson setup --prefix="${INSTALL_PREFIX}" build/ ) && \ 265 | meson compile -C build/ ${JOBS:-} && \ 266 | sudo meson install --no-rebuild -C build/ 267 | elif [[ -f dub.json ]] 268 | then 269 | dub build --parallel 270 | fi 271 | # error out noisily if any of the build steps fail 272 | if [[ $? -gt 0 ]]; then 273 | failMsg "\n Building ${1} failed!\n '- Aborting!\n" 274 | fi 275 | popd 276 | } 277 | 278 | function buildAllDLangTools () 279 | { 280 | # We can do this because this invocation doesn't touch existing 281 | # bin dir/symlink 282 | mkdir -pv ${INSTALL_PREFIX}/bin 283 | echo -e "\nBuilding and installing libmoss, moss-service, avalanche, vessel and summit...\n" 284 | for repo in libmoss moss-service avalanche vessel summit; do 285 | buildDLangTool "$repo" 286 | done 287 | echo -e "\nSuccessfully built libmoss, moss-service, avalanche, vessel and summit.\n" 288 | #ls -lF ${INSTALL_PREFIX}/bin/{avalanche,vessel,summit} 289 | } 290 | 291 | function buildRustTools () 292 | { 293 | local repo=moss 294 | echo -e "\nBuilding and installing moss...\n" 295 | isGitRepo "$repo" || \ 296 | failMsg "${repo} does not appear to be a serpent tooling repo?" 297 | # Make the user deal with unclean git repos 298 | checkGitStatusClean "${repo}" 299 | 300 | pushd "${repo}" 301 | echo -e "\nResetting ownership as a precaution ...\n" 302 | sudo chown -Rc ${USER}:${USER} * 303 | echo -e "\nConfiguring, building and installing ${repo} ...\n" 304 | rm -v target/{debug,release}/moss 305 | cargo build -p moss && \ 306 | sudo install -Dm00755 target/debug/moss /usr/bin/moss 307 | # error out noisily if any of the build steps fail 308 | if [[ $? -gt 0 ]]; then 309 | failMsg "\n Building ${1} failed!\n '- Aborting!\n" 310 | fi 311 | popd 312 | echo -e "\nSuccessfully built and installed moss:\n" 313 | ls -lF ${INSTALL_PREFIX}/bin/moss 314 | } 315 | 316 | function cleanTool () 317 | { 318 | isGitRepo "${1}" || \ 319 | failMsg "${1} does not appear to be a serpent tooling repo?" 320 | 321 | pushd "${1}" 322 | if [[ -d build/ ]]; then 323 | echo -e "\nUninstalling ${1} ...\n" 324 | sudo ninja uninstall -C build/ 325 | echo -e "\nResetting permissions for ${1} ...\n" 326 | sudo chown -Rc ${USER}:${USER} * 327 | echo -e "\nCleaning ${1}/build/ ...\n" 328 | meson compile --clean -C build/ 329 | echo -e "\nDone.\n" 330 | else 331 | echo -e "\nCan't clean non-existing ${1}/build/ directory.\n" 332 | fi 333 | popd 334 | } 335 | 336 | function cleanAllDlangTools () 337 | { 338 | echo -e "\nRunning 'meson compile --clean' for all serpent repos...\n" 339 | for repo in boulder libstone moss-container; do 340 | cleanTool "$repo" 341 | done 342 | 343 | } 344 | 345 | REPO_FAIL=() 346 | # Will likely fail if the repo path exists locally, 347 | # so this may not be a good solution 348 | function cloneRepo() 349 | { 350 | # We want to run this from a clean clone root dir that isn't a git repo 351 | isGitRepo . && \ 352 | failMsg "Found a .git/ dir -- please run ${0} from the (unversioned) base serpent-os/ dir." 353 | 354 | echo -e "Cloning ${HTTPS_PREFIX}/${1}.git..." 355 | git clone --recurse-submodules "${HTTPS_PREFIX}/${1}.git" 356 | # Only set up push URI on successful clone 357 | if [[ $? -eq 0 ]]; then 358 | echo -e "\nSetting up ${1} SSH push URI...\n" 359 | git -C "${1}" remote set-url --push origin "${SSH_PREFIX}/${1}.git" 360 | git -C "${1}" remote -v 361 | echo "" 362 | else 363 | echo -e "\n- failed to git clone --recurse-submodules ${1},\n" 364 | echo -e "-- NOT attempting to set push URI for ${1}.\n" 365 | REPO_FAIL+=("${1}") 366 | fi 367 | } 368 | 369 | # Takes a single argument, which is the name of an existing known dir 370 | # with a .git/ dir 371 | function pullRepo() 372 | { 373 | isGitRepo "${1}" || \ 374 | failMsg "${1} does not appear to be a valid repo for git pull? Aborting." 375 | checkGitStatusClean "${1}" 376 | checkoutRef ${1} 377 | 378 | pushd "${1}" 379 | git pull --rebase --recurse-submodules 380 | if [[ $? -eq 0 ]]; then 381 | echo -e "\nChecking ${1} SSH push URI...\n" 382 | local PUSH_URI="$(git remote get-url --push origin)" 383 | # Don't touch the push URI if the user has manually re-configured it 384 | # to a different SSH push URI than the default 385 | if [[ "${PUSH_URI}" =~ "git@github.com:" && ! "${PUSH_URI}" =~ "serpent-os" ]]; then 386 | echo "- Push URI for ${1} has been changed manually, not attempting to reset it." 387 | else 388 | # Reset push URI on the off chance that the current repo 389 | # has been recloned manually 390 | echo "- Resetting ${1} push URI to default..." 391 | git remote set-url --push origin "${SSH_PREFIX}/${1}.git" 392 | fi 393 | git remote -v 394 | echo "" 395 | else 396 | # We deliberately drop into the offending git repo 397 | echo -e "\n- failed to git pull --rebase --recurse-submodules ${1}" 398 | echo -e "-- NOT attempting to set push URI for ${1}.\n" 399 | REPO_FAIL+=("${1}") 400 | fi 401 | popd 402 | } 403 | 404 | # Make it easier to do automated checkouts and builds of branches 405 | # (useful for testing PRs spannning individual repo boundaries) 406 | function checkoutRef () 407 | { 408 | local branch="${CORE_REPOS[${1}]}" 409 | echo -e "\nChecking out the ${1} ${branch} branch/tag" 410 | git -C "${1}" checkout "${branch}" || \ 411 | failMsg "- failed to git checkout the ${branch} branch/tag for ${1}!" 412 | echo "" 413 | } 414 | 415 | function activateCommitHooks () 416 | { 417 | isGitRepo "${1}" || \ 418 | failMsg "${1} does not appear to be a valid repo for adding git commit hooks? Aborting." 419 | 420 | pushd "${1}" 421 | local addHooks="./serpent-style/activate-git-hooks.sh" 422 | if [[ -x "${addHooks}" ]]; then 423 | ${addHooks} 424 | else 425 | ls -l "${addHooks}" 426 | fi 427 | echo -e "\nActive serpent-style git hooks in ${PWD}:\n" 428 | ls -l .git/hooks/ |grep 'serpent-style' 429 | echo "" 430 | popd 431 | } 432 | 433 | function updateRepo () 434 | { 435 | isGitRepo "${1}" && pullRepo "${1}" || cloneRepo "${1}" 436 | if [[ $? -eq 0 ]]; then 437 | activateCommitHooks "${1}" 438 | fi 439 | } 440 | 441 | function updateAllRepos () 442 | { 443 | echo -e "\nUpdating all serpent tooling repos to newest upstream version...\n" 444 | for repo in ${!CORE_REPOS[@]}; do 445 | updateRepo "$repo" 446 | done 447 | # If we have a non-empty REPO_FAIL array, we're in trouble 448 | [[ ${#REPO_FAIL[@]} -gt 0 ]] && failMsg "ERROR:\n\nFailed to update repos:\n\n${REPO_FAIL[@]}\n" 449 | 450 | echo -e "List of directories in ${RUN_DIR}:\n" 451 | ls -1F --group-directories-first ${RUN_DIR} 452 | 453 | echo -e "\nAll serpent tooling repos successfully updated to newest upstream version.\n" 454 | } 455 | 456 | function pushRepo() 457 | { 458 | isGitRepo "${1}" || \ 459 | failMsg "${1} does not appear to be a valid repo for git push? Aborting." 460 | checkGitStatusClean "${1}" 461 | 462 | pushd "${1}" 463 | 464 | git push 465 | if [[ $? -gt 0 ]]; then 466 | # We deliberately drop into the offending git repo 467 | failMsg "Failed to run git push for ${1}. Aborting." 468 | fi 469 | popd 470 | } 471 | 472 | function pushAllRepos () 473 | { 474 | echo -e "\nPushing all local commits to upstream repos...\n" 475 | for repo in ${CORE_REPOS[@]}; do 476 | pushRepo "$repo" 477 | done 478 | echo -e "\nSuccessfully pushed newest local code to all serpent tooling repos.\n" 479 | } 480 | 481 | function updateUsage () 482 | { 483 | MSG=" 484 | To check if all prerequisites are available on the local system, 485 | run 'onboarding/check-prereqs.sh'. 486 | 487 | To build the currently checked out versions of the Serpent OS service tooling, 488 | run 'onboarding/build-all.sh'. 489 | 490 | Developers with commit access can use 'onboarding/push-all.sh' to push all 491 | local changes in sequence when working on feature/topic branches. 492 | 493 | To update all repos and build the newest version of the Serpent OS service 494 | tooling, simply run './update.sh' from the serpent-os/ clone root. 495 | 496 | Most people should only need to use './update.sh'. 497 | " 498 | echo -e "${MSG}" 499 | } 500 | --------------------------------------------------------------------------------