├── .gitignore ├── CHANGELOG.md ├── HOWTO-libvirt.md ├── LICENSE ├── README.md ├── helpers ├── Makefile ├── enter-grain-source │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── enter_grain └── enter_grain.sha1 ├── release.sh ├── stacks ├── diy │ ├── build.sh │ ├── launcher.sh │ └── setup.sh ├── golang │ ├── build.sh │ ├── launcher.sh │ └── setup.sh ├── lemp │ ├── build.sh │ ├── launcher.sh │ ├── service-config │ │ ├── mime.types │ │ └── nginx.conf │ └── setup.sh ├── lesp │ ├── build.sh │ ├── launcher.sh │ ├── service-config │ │ ├── mime.types │ │ └── nginx.conf │ └── setup.sh ├── meteor │ ├── build.sh │ ├── initargs │ ├── launcher.sh │ └── setup.sh ├── node │ ├── build.sh │ ├── launcher.sh │ └── setup.sh ├── static │ ├── launcher.sh │ ├── service-config │ │ ├── mime.types │ │ └── nginx.conf │ └── setup.sh └── uwsgi │ ├── build.sh │ ├── launcher.sh │ ├── service-config │ ├── mime.types │ └── nginx.conf │ └── setup.sh ├── vagrant-spk └── windows-support ├── Makefile ├── modpath.iss └── windows-installer.iss /.gitignore: -------------------------------------------------------------------------------- 1 | # The following files are auto-generated from the build process for 2 | # the Windows vagrant-spk EXE. 3 | build/ 4 | vendor/ 5 | windows-support/state/ 6 | dist/ 7 | vagrant-spk.spec 8 | 9 | # The following file patterns are auto-created by editors like Emacs. 10 | .#* 11 | *~ 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### v1.2 (2024-03-16) 2 | - Updated base image to Debian Bookworm. 3 | - Configured MySQL stacks to only initialize the database once. 4 | - Removed legacy node6 stack. 5 | - Updated meteor stack to use the latest meteor-spk. 6 | 7 | ### v1.1.2 (2023-11-01) 8 | - Updated Windows build to Python 3.11.6. 9 | - Updated base image to Debian Bullseye. 10 | - Updated golang stack to use the latest Go. 11 | - Updated node stack to use new installation script. 12 | - Updated uwsgi stack to Python 3. 13 | - Configured stacks with Nginx or MySQL to rotate log files by default. (Thanks @zenhack) 14 | - Fix issue with synced folder which can cause Sandstorm to fail on boot. (Thanks @troyjfarrell) 15 | - Fix issue with upgrading Virtualbox Guest Additions causing startup to hang. 16 | - Fix `upgradevm` command. (Thanks @zenhack) 17 | - Fix link to running server in terminal. (Thanks @zenhack) 18 | - Exclude Vagrant log files in gitignore. (Thanks @zenhack) 19 | 20 | ### v1.1.1 (2021-04-19) 21 | - Updated Windows build to Python 3.8.9. 22 | - No longer includes ssh.exe in Windows release. 23 | 24 | ### v1.1 (2021-04-10) 25 | - Upgraded vagrant-spk to Python 3.x. (Thanks @troyjfarrell) 26 | - Upgraded base image to Debian Buster. 27 | - Displays URL of test URL in the CLI. (Thanks @zdb999) 28 | - Blocks client-side third party content by default. 29 | - Fix edge case in packing spk to a different volume. (Thanks @zenhack) 30 | - Various stack upgrades and improvements. (Thanks @zenhack) 31 | - Cleaned up release process significantly. (Thanks @paulproteus and @zenhack) 32 | 33 | ### v1.0 (2020-02-16) 34 | - Changed vagrant-spk's preferred port to 6090 to avoid conflicting with Sandstorm. 35 | - Added `upgradevm` command to upgrade to latest compatible VM. 36 | - Updated Node stack to Node 10, and fixed legacy Node 6 stack. (Thanks @curbengh) 37 | - Fixed detection of VM configurations that do not work with vagrant-spk. 38 | - Fixed bugs preventing `enter-grain` from working. (Thanks @abliss and @zenhack) 39 | - Fixed bug causing VM to allocate too little RAM on Windows. 40 | 41 | ### v0.236 (2018-07-08) 42 | - Updated multiple stacks for compatibility with Debian stretch. 43 | - Added `listkeys` command to show which app keys are in your keyring. 44 | - Added ability to check vagrant-spk version with `--version`. 45 | - Fix bug where `curl` cannot be downloaded. 46 | - Fix bug caused by Vagrant version 2.0.3 and later. 47 | 48 | ### v0.230 (2018-03-17) 49 | - Now using Debian Contrib base image rather than a custom image. 50 | - Fixed various bitrot. 51 | - Other fixes, see git history. 52 | 53 | ### v0.186 (2016-09-21) 54 | - BUG FIX: 55 | - All stacks embedding MySQL now use `/var/tmp` for temporary storage. Thanks 56 | @FiloSottile for reporting a Piwik issue that enabled us to notice this problem 57 | and fix it for all future packages. Note that newly-created apps in these platform 58 | stacks (`lemp`, `uwsgi`) will clear the `/var/tmp` directory every time the grain 59 | starts. 60 | 61 | ### v0.165 (2016-06-07) 62 | - BUG FIXES: 63 | - `vagrant-spk enter-grain` was basically 100% broken in v0.164, with some extra breakage on 64 | Windows. This release fixes that. Huge thanks to @ocdtrekkie for testing, to find the 65 | bugs. 66 | 67 | ### v0.164 (2016-05-27) 68 | - New features: 69 | - Add "vagrant-spk enter-grain" so developers can get a shell within a running grain. 70 | Thanks @zarvox for extensive review. 71 | - BUG FIXES: 72 | - Update example to refer to `vm` where necessary. Thanks @pgrm. 73 | - Update libvirt README to refer to `vm` where necessary. Thanks @techtonik. 74 | 75 | ### v0.161 (2016-05-02) 76 | - BUG FIX: 77 | - Fix typo in deprecation warning when running: `vagrant-spk global-status`. 78 | - Usability refinement: 79 | - Going forward, pipe `curl` output through `cat` so that it is 80 | more aggressively buffered and Vagrant shows it on one line. 81 | See: #158 82 | 83 | ### v0.159 (2016-04-22) 84 | - BUG FIX: 85 | - "vagrant-spk setupvm" would reliably crash on Windows 10, and 86 | perhaps other Windows systems. Bug reported by hexx on IRC; 87 | fixed by Drew Fisher. Thanks to hexx for the report. 88 | 89 | ### v0.155 (2016-03-28) 90 | - Two big user-facing changes: 91 | - Vagrant commands operate via "vagrant-spk vm {{commandName}}" now. 92 | For details: https://groups.google.com/forum/#!msg/sandstorm-dev/cuSNJ3IsP6I/26PzwiX4AgAJ 93 | - Use a .sandstorm/service-config/ directory for daemon config files, 94 | rather than asking users to monkey with /etc in a pseudo-deterministic 95 | fashion. 96 | - Docs improvements: Add a libvirt README to GitHub. 97 | - Usability refinements: 98 | - Network performance: Use PCNet-Fast III by default for virtual machines. 99 | - Error handling: exit(1) when specifying no stack to setupvm. 100 | - Interactive docs: 'vagrant-spk setupvm' now prints a list of known stacks. 101 | - Error handling: Stop crashing when user forgets filename for 'vagrant-spk publish'. 102 | - Default 'setup.sh' tells users they might need to re-run 'provision'. 103 | - Default apt configuration will retry on failure. 104 | - Provide a .sandstorm/.gitignore to avoid users committing useless cruft. 105 | - Support 'vagrant-spk keygen -- -q' for use by scripts. 106 | - Vagrantfile: Stop mounting '/vagrant' - this is a duplicate of /opt/app. 107 | - Python stack: install git by default, since pip/requirements.txt might need it. 108 | - Add PHP & sqlite stack, to minimize bloat for PHP apps that don't need MySQL. 109 | - Use 'rm -f' and 'ln -sf' for idempotence. 110 | - Set 'gzip off;' by default, to work around a Sandstorm bug that results 111 | in meaningless gobbledy-gook on error pages. 112 | 113 | ### v0.139 (2016-01-13) 114 | - New features: 115 | - Add nodejs stack by @mnutt. 116 | - Add `vagrant-spk verify` command by @zarvox. 117 | - BUG FIXES: 118 | - In PHP configuration file, use semicolon as comment marker, not hash sign. 119 | If you run into PHP errors related to this problem, you may need to 120 | re-generate your `.sandstorm/*.sh` scripts, and destroy & recreate your 121 | Vagrant box. Thanks to @ndarilek for finding. 122 | 123 | ### v0.137 (2015-12-16) 124 | - BREAKING CHANGE: Every Sandstorm app MUST change one line in `.sandstorm/Vagrantfile`. 125 | - **Change required**: Every app must edit `.sandstorm/Vagrantfile`. Find the line containing: 126 | - `config.vm.box = "debian/jessie64"` and replace it with 127 | - `config.vm.box = "sandstorm/debian-jessie64"` 128 | - Problem: Debian's official Vagrant base box (aka `debian/jessie64`) has stopped 129 | supporting VirtualBox file sharing. Specifically, version 8.2.2 of their base box 130 | made this change. This will result in sadness for `vagrant-spk` users: anyone who 131 | runs `vagrant-spk up` on a new system will get version 8.2.2 of the `debian/jessie64` 132 | base box, resulting in non-working VirtualBox file sharing, resulting in apps that 133 | fail to build. If you've run `vagrant box update`, you may have also downloaded version 134 | 8.2.2 of `debian/jessie64`, triggering the problem. 135 | - Solution: Sandstorm.io now maintains a 136 | [separate Vagrant base box](https://atlas.hashicorp.com/sandstorm/boxes/debian-jessie64) 137 | (called `sandstorm/debian-jessie64`) which does support VirtualBox file sharing. 138 | For now, this is a bit-for-bit copy of the most recent `debian/jessie64` base box 139 | that **did** support file sharing. Since Sandstorm now controls the base box, it is 140 | safe to run `vagrant box update` once you have changed to our base box. 141 | - Update `vagrant-spk up` to check for the above problem and inform people on 142 | how to fix it. Update auto-generated `Vagrantfile` accordingly as well. 143 | - For freshly-created Meteor apps, be a little less quiet so that people can 144 | understand how their package build is progressing. 145 | - (EXPERIMENTAL) Improvements to automatic Meteor app packaging, aka 146 | `vagrant-spk auto meteor`: 147 | - Automatically switch Google Fonts from HTTP to HTTPS. 148 | - Open `.meteor/` files in append mode, to avoid overwriting them. 149 | - Add more newlines when editing `.meteor/` files. 150 | 151 | ### v0.130 (2015-11-04) 152 | - (EXPERIMENTAL) vagrant-spk auto meteor improvements: 153 | - Automatically switch (some) HTTP resource references to HTTPS. 154 | - Add kentonv:accounts-sandstorm to generated packages. 155 | - Use git repo name to infer package name. 156 | - Store git repo URL in the package metadata. 157 | - Meteor stack: Use `meteor-spk` version 0.1.8. This fixes a bug where 158 | niscudb => Mongo 3 migrations would sometimes fail. If your app has 159 | `PACKAGE=meteor-spk-0.1.7` in `.sandstorm/setup.sh`, **and** if it 160 | ever had a previous version, then you should migrate to 161 | `PACKAGE=meteor-spk-0.1.8` and do `vagrant-spk destroy` to flush the 162 | cached meteor-spk package version in any packaging VMs. 163 | 164 | ### v0.125 (2015-10-22) 165 | - Provide a SANDSTORM=1 environment variable so apps can decide if they are 166 | running in Sandstorm, through a check at runtime. 167 | - Make the Meteor stack less verbose. 168 | - Avoid Vagrant's scary red colorization of stderr. 169 | - More useful help text; thanks @pwais. 170 | - By default, don't limit max body size for inbound HTTP messages in stacks 171 | that use nginx. This fixes a problem where apps generated by vagrant-spk 172 | could not accept large (>1MB) file uploads. 173 | - Upgrade meteor stack to meteor-spk 0.1.7, which results in Meteor apps 174 | getting MongoDB 3.x. 175 | - Make better VM names in VirtualBox, based on the path of the directory 176 | containing .sandstorm/ (typically the name of the app) and the current 177 | time (to avoid conflicts). 178 | - Provide more RAM & CPU on Windows-based vagrant-spk VMs, similar to how 179 | we calculate this on Linux. 180 | - Add experimental "auto" command, for automatic packaging of Meteor apps. 181 | 182 | ### v0.107 (2015-09-01) 183 | - Add "publish" command, so this can be used with https://apps.sandstorm.io/. 184 | - Fix small bugs: 185 | - atomically download Sandstorm tar.xz file. 186 | - remove "wipe" command, since only Drew ever used it. 187 | - LEMP stack fixes: 188 | - use `mkdir -p` to create sessions directory, to avoid confusing Debian's php package. 189 | - DIY stack fixes: 190 | - use "-y" in apt-get install examples, to avoid prompting for input. 191 | - Improve debugging: 192 | - send nginx logs to stderr. 193 | - stop storing nginx access logs. 194 | - Improve vagrant-spk build process: 195 | - Use fully-headless Windows package build process. 196 | - Move stack scripts into their own files for tidiness. 197 | 198 | ### v0.101 (2015-07-27) 199 | - Create Windows installer; start doing releases. 200 | 201 | ### v0.100 and earlier 202 | - Change logs were not kept, but you can inspect version history in git. 203 | -------------------------------------------------------------------------------- /HOWTO-libvirt.md: -------------------------------------------------------------------------------- 1 | # Using vagrant-spk with libvirt 2 | 3 | If you're running on Linux, you have the option of using `vagrant-spk` with Vagrant's libvirt 4 | backend. Some developers have performance or reliability issues with VirtualBox and prefer to use 5 | libvirt. 6 | 7 | Some caveats: 8 | 9 | * `libvirt` is not the default Vagrant backend, and is nowhere near as widely used as the VirtualBox 10 | backend. Things may break, either in Vagrant, `libvirt`, or in `vagrant-spk`. 11 | * You will have to do some additional up-front configuration, which may be annoying/burdensome. 12 | 13 | That said, Drew (@zarvox) uses the libvirt backend for all of his vagrant-spk work, and would prefer 14 | that it work for people in general, so if you're so inclined, try it out, and file bugs if you hit 15 | issues. Thanks! 16 | 17 | 18 | ## Install libvirt/libvirtd/virt-manager 19 | 20 | On Fedora, this would be: 21 | 22 | ```bash 23 | sudo dnf install virt-manager libvirt libvirt-daemon 24 | ``` 25 | 26 | On Debian/Ubuntu: 27 | 28 | ``` 29 | sudo apt-get install libvirt-daemon-system virt-manager 30 | ``` 31 | 32 | 33 | ## Install vagrant 34 | 35 | Fedora: 36 | 37 | ```bash 38 | sudo dnf install vagrant 39 | ``` 40 | 41 | 42 | ## Install vagrant-libvirt 43 | 44 | Fedora: 45 | 46 | ```bash 47 | sudo dnf install vagrant-libvirt 48 | ``` 49 | 50 | 51 | ## Disable sandboxing of your VM/SELinux enforcement 52 | 53 | This is necessary to allow mounting shared folders from your user's home directory (or wherever the 54 | app you're developing is located) read-write, and getting the correct permissions out the other 55 | side. 56 | 57 | Fedora: edit `/etc/libvirt/qemu.conf` and set the following keys (there are comment blocks for each 58 | in the default config): 59 | 60 | ``` 61 | security_driver = "none" 62 | user = "root" 63 | group = "root" 64 | dynamic_ownership = 0 65 | clear_emulator_capabilities = 0 66 | ``` 67 | 68 | * The first key says "disable SELinux enforcement". 69 | * The second and third say "run qemu-kvm as root", which is needed so that the process has 70 | `CAP_DAC_OVERRIDE` and can create files owned by your user account. 71 | * The fourth key says "don't change files to be owned by root". We want files owned by your user 72 | account, by and large. 73 | * The fifth key says "don't drop `CAP_DAC_OVERRIDE`". Linux has a curious feature which allows 74 | processes running as root to drop certain privileges. However, in this case, we need the one 75 | that allows you to read/modify/write files owned by other users (namely, the user you are doing 76 | development as). 77 | 78 | 79 | ## Install vagrant-mutate and import a box 80 | 81 | We need the `vagrant-mutate` plugin to import a box originally packaged for VirtualBox, convert it 82 | to the libvirt disk image format, and register it as the equivalent box name for Vagrant to use it. 83 | 84 | ### Install build dependencies: 85 | 86 | Fedora: 87 | 88 | ```bash 89 | sudo dnf install qemu-img libvirt-devel ruby-libvirt ruby-devel 90 | ``` 91 | 92 | ### Install the plugin: 93 | 94 | ```bash 95 | vagrant plugin install mutate 96 | ``` 97 | 98 | ### Import the VirtualBox box: 99 | 100 | ```bash 101 | vagrant box add sandstorm/debian-jessie64 102 | ``` 103 | 104 | ### Produce an appropriate box for usage with libvirt: 105 | 106 | ```bash 107 | vagrant mutate sandstorm/debian-jessie64 libvirt 108 | ``` 109 | 110 | 111 | ## Set libvirt default provider 112 | 113 | Fedora users can skip this step. 114 | 115 | To make sure `vagrant` knows to use the libvirt backend for managing VMs, you'll need to set an 116 | environment variable: 117 | 118 | ```bash 119 | export VAGRANT_DEFAULT_PROVIDER=libvirt 120 | ``` 121 | 122 | You'll probably want to put the above line in your `$HOME/.bashrc` so you don't have to type it every time 123 | you open a shell that you use `vagrant-spk` in. 124 | 125 | 126 | ## Use vagrant-spk with libvirt 127 | 128 | Now, whenever you use vagrant-spk, vagrant should attempt to use the libvirt backend when creating VMs. :) 129 | 130 | If you have an app where vagrant-spk previously created a VirtualBox VM, vagrant will continue to interact 131 | with that VM until you run `vagrant-spk vm destroy`, after which the next `vagrant-spk vm up` should create a libvirt VM. 132 | 133 | ## Optional: PolicyKit rule for quality-of-life improvement 134 | 135 | Under the hood, libvirt uses PolicyKit to check if your user is authorized to make changes to 136 | libvirt-managed VMs. If you are on a single-user machine, probably you don't want to have to 137 | enter your password or root's password every time you want to bring a VM up or down. 138 | 139 | To that end, you can whitelist your user for all libvirt actions by writing a PolicyKit rule 140 | and placing it in the appropriate folder for your system. For example, on Fedora, for a user 141 | named `zarvox`, you'd create (as root) a file `/etc/polkit-1/rules.d/10-libvirt-zarvox.rules` with 142 | the contents: 143 | 144 | ```javascript 145 | polkit.addRule(function(action, subject) { 146 | if (action.id == "org.libvirt.unix.manage" && subject.user == "zarvox") { 147 | return "yes"; 148 | } 149 | }); 150 | ``` 151 | 152 | and then restart polkitd: 153 | 154 | ```bash 155 | sudo systemctl restart polkit.service 156 | ``` 157 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2015-2018 Sandstorm Development Group, Inc. and contributors 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vagrant-spk 2 | 3 | `vagrant-spk` is a tool designed to help app developers package apps for [Sandstorm](https://sandstorm.io). 4 | 5 | ## Example usage: 6 | 7 | git clone git://github.com/sandstorm-io/vagrant-spk 8 | git clone git://github.com/sandstorm-io/php-app-to-package-for-sandstorm 9 | export PATH=$(pwd)/vagrant-spk:$PATH 10 | cd php-app-to-package-for-sandstorm 11 | vagrant-spk setupvm lemp 12 | vagrant-spk vm up 13 | vagrant-spk init 14 | # edit .sandstorm/sandstorm-pkgdef.capnp in your editor of choice 15 | vagrant-spk dev 16 | # visit http://local.sandstorm.io:6090 in a web browser 17 | # log in as Alice, the admin account 18 | # launch an instance of the example app, play around with it 19 | # then, press Ctrl-C to stop the tracing vagrant-spk dev 20 | vagrant-spk pack example.spk 21 | # You now have an .spk file. Yay! 22 | # Verify it works by going to http://local.sandstorm.io:6090, 23 | # select "My Files" -> "Upload an app", select your .spk file, 24 | # upload it, install it, and create a new instance of your app. 25 | 26 | ## What the files are for 27 | 28 | `vagrant-spk` will create a `.sandstorm/` folder in your repo and set up some 29 | files with some defaults for your app stack. You will likely need to modify 30 | some of these to adapt their behavior to make the most sense for your app. 31 | 32 | See the [vagrant-spk docs on customizing your 33 | package](https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/) 34 | for full details. 35 | 36 | ## Example apps 37 | 38 | See the [example app listing in the vagrant-spk 39 | documentation.](https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#example-setups) 40 | -------------------------------------------------------------------------------- /helpers/Makefile: -------------------------------------------------------------------------------- 1 | all: enter_grain.sha1 2 | 3 | .PHONY: enter_grain 4 | 5 | enter_grain: 6 | cd enter-grain-source && cargo build --release && strip target/release/enter_grain && cp target/release/enter_grain ../ 7 | 8 | enter_grain.sha1: enter_grain 9 | sha1sum enter_grain > enter_grain.sha1 10 | 11 | -------------------------------------------------------------------------------- /helpers/enter-grain-source/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | target -------------------------------------------------------------------------------- /helpers/enter-grain-source/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "enter_grain" 5 | version = "0.1.0" 6 | dependencies = [ 7 | "syscall 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 8 | ] 9 | 10 | [[package]] 11 | name = "syscall" 12 | version = "0.2.1" 13 | source = "registry+https://github.com/rust-lang/crates.io-index" 14 | 15 | [metadata] 16 | "checksum syscall 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dae2c4de039bf338dd96f46621f20222c4101045dac5403b46f472608cb5b556" 17 | -------------------------------------------------------------------------------- /helpers/enter-grain-source/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enter_grain" 3 | version = "0.1.0" 4 | authors = ["Asheesh Laroia"] 5 | 6 | [dependencies] 7 | syscall = "0.2.1" 8 | 9 | [profile.release] 10 | opt-level = 3 11 | lto = true 12 | -------------------------------------------------------------------------------- /helpers/enter-grain-source/src/main.rs: -------------------------------------------------------------------------------- 1 | // use std::env; 2 | use std::fs::{File, metadata}; 3 | use std::os::unix::fs::MetadataExt; 4 | use std::io::Read; 5 | use std::ptr; 6 | use std::env; 7 | 8 | #[macro_use] 9 | extern crate syscall; 10 | 11 | fn write(fd: usize, buf: &[u8]) { 12 | unsafe { 13 | syscall!(WRITE, fd, buf.as_ptr(), buf.len()); 14 | } 15 | } 16 | 17 | fn open_as_fd_or_die(filename: &[u8]) -> usize { 18 | let o_rdonly = 0u64; 19 | let no_flags = 0u64; 20 | unsafe { 21 | let return_value = syscall!(OPEN, filename.as_ptr(), o_rdonly, no_flags); 22 | sanity_check_fd(return_value); 23 | return return_value; 24 | } 25 | } 26 | 27 | fn setns(fd: usize, nstype: usize) { 28 | unsafe { 29 | syscall!(SETNS, fd, nstype); 30 | } 31 | } 32 | 33 | fn close(fd: usize) { 34 | unsafe { 35 | syscall!(CLOSE, fd); 36 | } 37 | } 38 | 39 | fn sanity_check_fd(fd: usize) { 40 | // For some reason, syscall!(OPEN) returns usize, but I need to check its output against 41 | // -1. So I'm going to just check if it's >255. 42 | if fd > 255 { 43 | panic!("Failed to open a needed file. Bailing."); 44 | } 45 | } 46 | 47 | fn setgroups_zero() { 48 | // Clears auxilary groups. Probably not necessary, but whatever. 49 | unsafe { 50 | syscall!(SETGROUPS, 0); 51 | } 52 | } 53 | 54 | fn fchdir(fd: usize) { 55 | unsafe { 56 | syscall!(FCHDIR, fd); 57 | } 58 | } 59 | 60 | fn fork() -> usize { 61 | unsafe { 62 | syscall!(FORK) 63 | } 64 | } 65 | 66 | fn wait_all_children() { 67 | let all_children = -1isize; 68 | let no_options = 0usize; 69 | let nullptr = 0usize; 70 | unsafe { 71 | syscall!(WAIT4, all_children, no_options, nullptr, nullptr); 72 | } 73 | } 74 | 75 | fn setuid_setgid() { 76 | let meta = metadata("/var").unwrap(); 77 | 78 | unsafe { 79 | syscall!(SETUID, meta.uid()); 80 | syscall!(SETGID, meta.gid()); 81 | } 82 | } 83 | 84 | fn execve_bash(envp: std::vec::Vec<*const u8>) { 85 | let bash_path = "/bin/bash\0".as_bytes(); 86 | let nullargv = 0usize; 87 | unsafe { 88 | // execve will not return at all in the case of success. In the case of failure it will 89 | // return -1. I check it against 0, which is the typical UNIX/C idiom. 90 | let retval = syscall!(EXECVE, bash_path.as_ptr(), nullargv, envp.as_ptr()); 91 | if retval != 0usize { 92 | panic!("Failed to find and launch bash within the grain. Bailing out now."); 93 | } 94 | } 95 | } 96 | 97 | fn get_envp(pid_str_ref: &str) -> std::vec::Vec<*const u8> { 98 | // Grab the environ from pid 1048 so that when we execve a shell 99 | // at the end, we can provide the environment. 100 | 101 | let path = "/proc/".to_string() + pid_str_ref + &"/environ".to_string(); 102 | match File::open(&path) { 103 | Err(why) => panic!("couldn't open {}: {}", path, why), 104 | Ok(mut file) => { 105 | let mut s = String::new(); 106 | match file.read_to_string(&mut s) { 107 | Err(why) => panic!("couldn't read {}: {}", path, why), 108 | Ok(_) => { 109 | let mut v: Vec = s.split('\0').map( 110 | |x| x.to_string() + "\0").collect(); 111 | v.push("__terminator".to_string()); 112 | let all_environ_arguments: Vec<*const u8> = v.iter().map(|x| ( 113 | if x == "__terminator" { ptr::null() } 114 | else { x.as_ptr() } 115 | )).collect(); 116 | return all_environ_arguments; 117 | } 118 | } 119 | } 120 | }; 121 | } 122 | 123 | fn main() { 124 | let pid_str_ref = &env::args().nth(1).expect("Panicking: expected argv to have 2 items.").to_string(); 125 | // Grab the environ from pid 1048 so that when we execve a shell 126 | // at the end, we can provide the environment. 127 | let result = get_envp(pid_str_ref); 128 | 129 | // let filename: &[u8] = b"/usr/bin/sensors\x00"; // <-- Make c strings like this 130 | // let argv1: &[u8] = b"/usr/bin/sensors\x00"; 131 | // let argv2: &[u8] = b"-h\x00"; 132 | // let argv: &[int] = [ // <-- store them in this 133 | // ::core::intrinsics::transmute(argv1.as_ptr()), // <-- transmuting 134 | // ::core::intrinsics::transmute(argv2.as_ptr()), 135 | // 0 // <-- and NULL terminate 136 | // ]; 137 | // let envp: &[int] = [0];let target_environ 138 | write(1, 139 | ("Attaching to process ID ".to_string() + pid_str_ref + &"...\n".to_string()).as_bytes()); 140 | let userns_fd = open_as_fd_or_die( 141 | ("/proc/".to_string() + pid_str_ref + "/ns/user\0").as_bytes()); 142 | let ipc_fd = open_as_fd_or_die( 143 | ("/proc/".to_string() + pid_str_ref + "/ns/ipc\0").as_bytes()); 144 | let uts_fd = open_as_fd_or_die( 145 | ("/proc/".to_string() + pid_str_ref + "/ns/uts\0").as_bytes()); 146 | let net_fd = open_as_fd_or_die( 147 | ("/proc/".to_string() + pid_str_ref + "/ns/net\0").as_bytes()); 148 | let pid_fd = open_as_fd_or_die( 149 | ("/proc/".to_string() + pid_str_ref + "/ns/pid\0").as_bytes()); 150 | let mnt_fd = open_as_fd_or_die( 151 | ("/proc/".to_string() + pid_str_ref + "/ns/mnt\0").as_bytes()); 152 | let cwd_fd = open_as_fd_or_die( 153 | ("/proc/".to_string() + pid_str_ref + "/cwd\0").as_bytes()); 154 | setgroups_zero(); 155 | setns(userns_fd, 0x10000000usize); // CLONE_NEWUSER 156 | close(userns_fd); 157 | setns(ipc_fd, 0x08000000usize); // CLONE_NEWIPC 158 | close(ipc_fd); 159 | setns(uts_fd, 0x04000000usize); // CLONE_NEWUTS 160 | close(uts_fd); 161 | setns(net_fd, 0x40000000usize); // CLONE_NEWNET 162 | close(net_fd); 163 | setns(pid_fd, 0x20000000usize); // CLONE_NEWPID 164 | close(pid_fd); 165 | setns(mnt_fd, 0x00020000usize); // CLONE_NEWNS which I guess is mount namespaces 166 | close(mnt_fd); 167 | fchdir(cwd_fd); 168 | close(cwd_fd); 169 | // fork, and do a handful of things in the child before we execve bash. 170 | let fork_result = fork(); 171 | if fork_result == 0 { 172 | // in the child 173 | setuid_setgid(); 174 | execve_bash(result); 175 | } else { 176 | // in the parent 177 | wait_all_children(); 178 | } 179 | } 180 | 181 | // open("/proc/1048/ns/user", O_RDONLY) = 3 182 | // open("/proc/1048/ns/ipc", O_RDONLY) = 4 183 | // open("/proc/1048/ns/uts", O_RDONLY) = 5 184 | // open("/proc/1048/ns/net", O_RDONLY) = 6 185 | // open("/proc/1048/ns/pid", O_RDONLY) = 7 186 | // open("/proc/1048/ns/mnt", O_RDONLY) = 8 187 | // open("/proc/1048/cwd", O_RDONLY) = 9 188 | // setgroups(0, []) = 0 189 | // setns(3, CLONE_NEWUSER) = 0 190 | // close(3) = 0 191 | // setns(4, CLONE_NEWIPC) = 0 192 | // close(4) = 0 193 | // setns(5, CLONE_NEWUTS) = 0 194 | // close(5) = 0 195 | // setns(6, CLONE_NEWNET) = 0 196 | // close(6) = 0 197 | // setns(7, CLONE_NEWPID) = 0 198 | // close(7) = 0 199 | // setns(8, CLONE_NEWNS) = 0 200 | // close(8) = 0 201 | // fchdir(9) = 0 202 | // close(9) = 0 203 | -------------------------------------------------------------------------------- /helpers/enter_grain: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sandstorm-io/vagrant-spk/145e545acfce8b3430f93007fa504f23a7781945/helpers/enter_grain -------------------------------------------------------------------------------- /helpers/enter_grain.sha1: -------------------------------------------------------------------------------- 1 | 3e1a80bd7ca1f61de6b3044e904947264e6f6e53 enter_grain 2 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | set -euo pipefail 3 | 4 | DRY_RUN="${DRY_RUN:-yes}" 5 | 6 | function assert_github_token_and_release_tool_present() { 7 | echo "**** Checking environment for GitHub token. ****" 8 | export | grep -q GITHUB_TOKEN= || (echo "Aiee, you should set a GITHUB_TOKEN environment variable." ; exit 1) 9 | 10 | echo "**** Checking path for GitHub release tool. ****" 11 | which gh >/dev/null || (echo "Aiee, you need a gh tool in the PATH."; exit 1) 12 | } 13 | 14 | function assert_git_state_is_clean() { 15 | if [[ "${SKIP_GIT_CLEAN_CHECK:-no}" == "yes" ]]; then 16 | echo "Skipping git clean check due to SKIP_GIT_CLEAN_CHECK=yes in environment." 17 | return 18 | fi 19 | 20 | if [ "x$(git status --porcelain)" != "x" ]; then 21 | echo "Please commit changes to git before releasing." >&2 22 | exit 1 23 | fi 24 | 25 | echo "**** Checking that you did a git push already... ****" 26 | local ORIGIN_MASTER_GIT_REVISION="$(git rev-parse origin/master)" 27 | local CURRENT_HEAD_GIT_REVISION="$(git rev-parse HEAD)" 28 | if [[ "$ORIGIN_MASTER_GIT_REVISION" == "$CURRENT_HEAD_GIT_REVISION" ]]; then 29 | echo " success." 30 | else 31 | echo " fail. Please do a git push and re-run this script." 32 | exit 1 33 | fi 34 | } 35 | 36 | function get_release_name() { 37 | # TAG_NAME gets used as the git tag name 38 | TAG_NAME="$(./vagrant-spk --version | awk '{print $2}')" 39 | 40 | # DISPLAY_VERSION gets used in the git tag description 41 | DISPLAY_VERSION="${TAG_NAME:1}" 42 | } 43 | 44 | function assert_changelog_present() { 45 | if [[ "${SKIP_CHANGELOG_CHECK:-no}" == "yes" ]]; then 46 | echo "Skipping changelog check due to SKIP_CHANGELOG_CHECK=yes in environment." 47 | return 48 | fi 49 | 50 | # Verify that the changelog has been updated. 51 | EXPECTED_CHANGELOG="### $TAG_NAME ($(date '+%Y-%m-%d'))" 52 | if [ "$(head -n 1 CHANGELOG.md)" != "$EXPECTED_CHANGELOG" ]; then 53 | echo "Changelog not updated. First line should be:" >&2 54 | echo "$EXPECTED_CHANGELOG" >&2 55 | exit 1 56 | fi 57 | } 58 | 59 | function build_windows_exe() { 60 | # The Windows EXE filename is always vagrant-spk-setup.exe locally, and when we upload it to 61 | # GitHub as a release artifact, we rename it to use $TAG_NAME in the filename to indicate the 62 | # version. 63 | WINDOWS_EXE_PATH=windows-support/dist/innosetup 64 | 65 | echo "**** Building Windows EXE ****" 66 | (cd windows-support && make) 67 | } 68 | 69 | function tag_and_push() { 70 | echo "**** Tagging this commit ****" 71 | 72 | # The git tag stores the version number as a normal-looking version number, like 0.75 for build 75 73 | # within branch 0, or 2.121 for build 121 within branch 2. 74 | GIT_REVISION="$(git rev-parse HEAD)" 75 | 76 | if [[ "$DRY_RUN" == "yes" ]] ; then 77 | echo "Not tagging yet, but this would be vagrant-spk ${DISPLAY_VERSION}" 78 | echo "" 79 | else 80 | git tag "$TAG_NAME" "$GIT_REVISION" -m "Release vagrant-spk ${DISPLAY_VERSION}" 81 | fi 82 | 83 | echo "**** Pushing build $TAG_NAME ****" 84 | if [[ "$DRY_RUN" == "yes" ]]; then 85 | echo "Not pushing $TAG_NAME yet. Re-run with DRY_RUN=no in the environment." 86 | echo "" 87 | else 88 | git push origin "$TAG_NAME" 89 | fi 90 | } 91 | 92 | function create_github_release() { 93 | echo "**** Creating GitHub release for $TAG_NAME ****" 94 | 95 | if [[ "$DRY_RUN" == "yes" ]] ; then 96 | echo "Not creating GitHub release yet. Re-run with DRY_RUN=no in the environment." 97 | echo "" 98 | else 99 | mv "$WINDOWS_EXE_PATH/vagrant-spk-setup.exe" "$WINDOWS_EXE_PATH/vagrant-spk-setup-$TAG_NAME.exe" 100 | gh release create "$TAG_NAME" "$WINDOWS_EXE_PATH/vagrant-spk-setup-$TAG_NAME.exe" -d -R sandstorm-io/vagrant-spk -n "$(python3 -c 's = open("CHANGELOG.md").read(); print (s[:s.index("\n### ")-1])')" 101 | fi 102 | } 103 | 104 | function main() { 105 | assert_github_token_and_release_tool_present 106 | assert_git_state_is_clean 107 | get_release_name 108 | assert_changelog_present 109 | build_windows_exe 110 | tag_and_push 111 | create_github_release 112 | } 113 | 114 | main 115 | -------------------------------------------------------------------------------- /stacks/diy/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | # This script is run in the VM each time you run `vagrant-spk dev`. This is 4 | # the ideal place to invoke anything which is normally part of your app's build 5 | # process - transforming the code in your repository into the collection of files 6 | # which can actually run the service in production 7 | # 8 | # Some examples: 9 | # 10 | # * For a C/C++ application, calling 11 | # ./configure && make && make install 12 | # * For a Python application, creating a virtualenv and installing 13 | # app-specific package dependencies: 14 | # virtualenv /opt/app/env 15 | # /opt/app/env/bin/pip install -r /opt/app/requirements.txt 16 | # * Building static assets from .less or .sass, or bundle and minify JS 17 | # * Collecting various build artifacts or assets into a deployment-ready 18 | # directory structure 19 | 20 | # By default, this script does nothing. You'll have to modify it as 21 | # appropriate for your application. 22 | cd /opt/app 23 | exit 0 24 | -------------------------------------------------------------------------------- /stacks/diy/launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | # This script is run every time an instance of our app - aka grain - starts up. 4 | # This is the entry point for your application both when a grain is first launched 5 | # and when a grain resumes after being previously shut down. 6 | # 7 | # This script is responsible for launching everything your app needs to run. The 8 | # thing it should do *last* is: 9 | # 10 | # * Start a process in the foreground listening on port 8000 for HTTP requests. 11 | # 12 | # This is how you indicate to the platform that your application is up and 13 | # ready to receive requests. Often, this will be something like nginx serving 14 | # static files and reverse proxying for some other dynamic backend service. 15 | # 16 | # Other things you probably want to do in this script include: 17 | # 18 | # * Building folder structures in /var. /var is the only non-tmpfs folder 19 | # mounted read-write in the sandbox, and when a grain is first launched, it 20 | # will start out empty. It will persist between runs of the same grain, but 21 | # be unique per app instance. That is, two instances of the same app have 22 | # separate instances of /var. 23 | # * Preparing a database and running migrations. As your package changes 24 | # over time and you release updates, you will need to deal with migrating 25 | # data from previous schema versions to new ones, since users should not have 26 | # to think about such things. 27 | # * Launching other daemons your app needs (e.g. mysqld, redis-server, etc.) 28 | 29 | # By default, this script does nothing. You'll have to modify it as 30 | # appropriate for your application. 31 | cd /opt/app 32 | exit 0 33 | -------------------------------------------------------------------------------- /stacks/diy/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # When you change this file, you must take manual action. Read this doc: 4 | # - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh 5 | 6 | set -euo pipefail 7 | # This is the ideal place to do things like: 8 | # 9 | # export DEBIAN_FRONTEND=noninteractive 10 | # apt-get update 11 | # apt-get install -y nginx nodejs nodejs-legacy python2.7 mysql-server 12 | # 13 | # If the packages you're installing here need some configuration adjustments, 14 | # this is also a good place to do that: 15 | # 16 | # sed --in-place='' \ 17 | # --expression 's/^user www-data/#user www-data/' \ 18 | # --expression 's#^pid /run/nginx.pid#pid /var/run/nginx.pid#' \ 19 | # --expression 's/^\s*error_log.*/error_log stderr;/' \ 20 | # --expression 's/^\s*access_log.*/access_log off;/' \ 21 | # /etc/nginx/nginx.conf 22 | 23 | # By default, this script does nothing. You'll have to modify it as 24 | # appropriate for your application. 25 | exit 0 26 | -------------------------------------------------------------------------------- /stacks/golang/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | cd /opt/app 5 | if [ ! -e go.mod ]; then 6 | printf "Error: This directory does not contain a go module;\n" 7 | printf "vagrant-spk's golang stack does not support older GOPATH\n" 8 | printf "based projects. Try running:\n" >&2 9 | printf "\n" >&2 10 | printf " vagrant-spk vm ssh\n" >&2 11 | printf " cd /opt/app\n" >&2 12 | printf " go mod init example.com/mypkg\n" >&2 13 | exit 1 14 | fi 15 | go build -o app 16 | exit 0 17 | -------------------------------------------------------------------------------- /stacks/golang/launcher.sh: -------------------------------------------------------------------------------- 1 | exec /opt/app/app 2 | -------------------------------------------------------------------------------- /stacks/golang/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # When you change this file, you must take manual action. Read this doc: 4 | # - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh 5 | 6 | set -euo pipefail 7 | 8 | # The version of golang in the debian repositories tends to be incredibly 9 | # out of date; let's get ourselves a newer version from upstream: 10 | if [ -e /opt/app/.sandstorm/go-version ]; then 11 | # Get the same version we've used before 12 | curl -L "https://go.dev/dl/$(cat '/opt/app/.sandstorm/go-version').linux-amd64.tar.gz" -o go.tar.gz 13 | else 14 | # Get the newest version for a new project 15 | curl -L "https://go.dev/dl/$(curl 'https://go.dev/VERSION?m=text' | head -n 1).linux-amd64.tar.gz" -o go.tar.gz 16 | fi 17 | tar -C /usr/local -xzf go.tar.gz 18 | rm go.tar.gz 19 | echo 'export PATH=/usr/local/go/bin:$PATH' > /etc/profile.d/go.sh 20 | 21 | # Get the same version next time 22 | /usr/local/go/bin/go version | cut -d ' ' -f 3 > /opt/app/.sandstorm/go-version 23 | 24 | # Needed for fetching go libraries: 25 | apt-get install -y git 26 | 27 | exit 0 28 | -------------------------------------------------------------------------------- /stacks/lemp/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Checks if there's a composer.json, and if so, installs/runs composer. 3 | 4 | set -euo pipefail 5 | 6 | cd /opt/app 7 | 8 | if [ -f /opt/app/composer.json ] ; then 9 | if [ ! -f composer.phar ] ; then 10 | curl -sS https://getcomposer.org/installer | php 11 | fi 12 | php composer.phar install 13 | fi 14 | -------------------------------------------------------------------------------- /stacks/lemp/launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | wait_for() { 6 | local service=$1 7 | local file=$2 8 | while [ ! -e "$file" ] ; do 9 | echo "waiting for $service to be available at $file." 10 | sleep .1 11 | done 12 | } 13 | 14 | # Create a bunch of folders under the clean /var that php, nginx, and mysql expect to exist 15 | mkdir -p /var/lib/mysql 16 | mkdir -p /var/lib/mysql-files 17 | mkdir -p /var/lib/nginx 18 | mkdir -p /var/lib/php/sessions 19 | mkdir -p /var/log 20 | mkdir -p /var/log/mysql 21 | mkdir -p /var/log/nginx 22 | # Wipe /var/run, since pidfiles and socket files from previous launches should go away 23 | # TODO someday: I'd prefer a tmpfs for these. 24 | rm -rf /var/run 25 | mkdir -p /var/run/php 26 | rm -rf /var/tmp 27 | mkdir -p /var/tmp 28 | mkdir -p /var/run/mysqld 29 | 30 | # Rotate log files larger than 512K 31 | log_files="$(find /var/log -type f -name '*.log')" 32 | for f in $log_files; do 33 | if [ $(du -b "$f" | awk '{print $1}') -ge $((512 * 1024)) ] ; then 34 | mv $f $f.1 35 | fi 36 | done 37 | 38 | if [ ! -d /var/lib/mysql/mysql ]; then 39 | # Ensure mysql tables created 40 | HOME=/etc/mysql /usr/sbin/mysqld --initialize 41 | fi 42 | 43 | # Spawn mysqld, php 44 | HOME=/etc/mysql /usr/sbin/mysqld --skip-grant-tables & 45 | /usr/sbin/php-fpm8.2 --nodaemonize --fpm-config /etc/php/8.2/fpm/php-fpm.conf & 46 | # Wait until mysql has bound its socket, indicating readiness 47 | wait_for mysql /var/run/mysqld/mysqld.sock 48 | 49 | # # Uncomment this block if you need to preload a database for your app 50 | # if [ ! -e /var/.db-created ]; then 51 | # mysql --user root -e 'CREATE DATABASE app' 52 | # mysql --user root --database app < /opt/app/install.sql 53 | # touch /var/.db-created 54 | # fi 55 | 56 | # Wait until php has bound its socket, indicating readiness 57 | wait_for php-fpm8.2 /var/run/php/php8.2-fpm.sock 58 | 59 | # Start nginx. 60 | /usr/sbin/nginx -c /opt/app/.sandstorm/service-config/nginx.conf -g "daemon off;" 61 | -------------------------------------------------------------------------------- /stacks/lemp/service-config/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /stacks/lemp/service-config/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 4; 2 | pid /var/run/nginx.pid; 3 | 4 | events { 5 | worker_connections 768; 6 | # multi_accept on; 7 | } 8 | 9 | http { 10 | # Basic Settings 11 | sendfile on; 12 | tcp_nopush on; 13 | tcp_nodelay on; 14 | keepalive_timeout 65; 15 | types_hash_max_size 2048; 16 | # server_names_hash_bucket_size 64; 17 | server_tokens off; 18 | server_name_in_redirect off; 19 | 20 | include mime.types; 21 | default_type application/octet-stream; 22 | 23 | # Logging 24 | access_log off; 25 | error_log stderr; 26 | 27 | # Prevent nginx from adding compression; this interacts badly with Sandstorm 28 | # WebSession due to https://github.com/sandstorm-io/sandstorm/issues/289 29 | gzip off; 30 | 31 | # Trust the sandstorm-http-bridge's X-Forwarded-Proto. 32 | map $http_x_forwarded_proto $fe_https { 33 | default ""; 34 | https on; 35 | } 36 | 37 | server { 38 | listen 8000 default_server; 39 | listen [::]:8000 default_server ipv6only=on; 40 | 41 | # Allow arbitrarily large bodies - Sandstorm can handle them, and requests 42 | # are authenticated already, so there's no reason for apps to add additional 43 | # limits by default. 44 | client_max_body_size 0; 45 | 46 | server_name localhost; 47 | root /opt/app; 48 | location / { 49 | index index.php; 50 | try_files $uri $uri/ =404; 51 | } 52 | location ~ \.php$ { 53 | fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; 54 | fastcgi_index index.php; 55 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 56 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 57 | fastcgi_param QUERY_STRING $query_string; 58 | fastcgi_param REQUEST_METHOD $request_method; 59 | fastcgi_param CONTENT_TYPE $content_type; 60 | fastcgi_param CONTENT_LENGTH $content_length; 61 | 62 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 63 | fastcgi_param REQUEST_URI $request_uri; 64 | fastcgi_param DOCUMENT_URI $document_uri; 65 | fastcgi_param DOCUMENT_ROOT $document_root; 66 | fastcgi_param SERVER_PROTOCOL $server_protocol; 67 | fastcgi_param HTTPS $fe_https if_not_empty; 68 | 69 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 70 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 71 | 72 | fastcgi_param REMOTE_ADDR $remote_addr; 73 | fastcgi_param REMOTE_PORT $remote_port; 74 | fastcgi_param SERVER_ADDR $server_addr; 75 | fastcgi_param SERVER_PORT $server_port; 76 | fastcgi_param SERVER_NAME $server_name; 77 | 78 | # PHP only, required if PHP was built with --enable-force-cgi-redirect 79 | fastcgi_param REDIRECT_STATUS 200; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /stacks/lemp/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # When you change this file, you must take manual action. Read this doc: 4 | # - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh 5 | 6 | set -euo pipefail 7 | 8 | export DEBIAN_FRONTEND=noninteractive 9 | 10 | echo -e "deb http://repo.mysql.com/apt/debian/ bookworm mysql-8.0\ndeb-src http://repo.mysql.com/apt/debian/ bookworm mysql-8.0" > /etc/apt/sources.list.d/mysql.list 11 | wget -O /tmp/RPM-GPG-KEY-mysql https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 12 | apt-key add /tmp/RPM-GPG-KEY-mysql 13 | 14 | apt-get update 15 | apt-get install -y nginx php-fpm php-mysql php-cli php-curl git php-dev mysql-server 16 | service nginx stop 17 | service php8.2-fpm stop 18 | service mysql stop 19 | systemctl disable nginx 20 | systemctl disable php8.2-fpm 21 | systemctl disable mysql 22 | # patch /etc/php/8.2/fpm/pool.d/www.conf to not change uid/gid to www-data 23 | sed --in-place='' \ 24 | --expression='s/^listen.owner = www-data/;listen.owner = www-data/' \ 25 | --expression='s/^listen.group = www-data/;listen.group = www-data/' \ 26 | --expression='s/^user = www-data/;user = www-data/' \ 27 | --expression='s/^group = www-data/;group = www-data/' \ 28 | /etc/php/8.2/fpm/pool.d/www.conf 29 | # patch /etc/php/8.2/fpm/php-fpm.conf to not have a pidfile 30 | sed --in-place='' \ 31 | --expression='s/^pid =/;pid =/' \ 32 | /etc/php/8.2/fpm/php-fpm.conf 33 | # patch /etc/php/8.2/fpm/php-fpm.conf to place the sock file in /var 34 | sed --in-place='' \ 35 | --expression='s/^listen = \/run\/php\/php8.2-fpm.sock/listen = \/var\/run\/php\/php8.2-fpm.sock/' \ 36 | /etc/php/8.2/fpm/pool.d/www.conf 37 | # patch /etc/php/8.2/fpm/pool.d/www.conf to no clear environment variables 38 | # so we can pass in SANDSTORM=1 to apps 39 | sed --in-place='' \ 40 | --expression='s/^;clear_env = no/clear_env=no/' \ 41 | /etc/php/8.2/fpm/pool.d/www.conf 42 | # patch mysql conf to not change uid, and to use /var/tmp over /tmp 43 | # for secure-file-priv see https://github.com/sandstorm-io/vagrant-spk/issues/195 44 | sed --in-place='' \ 45 | --expression='s/^user\t\t= mysql/#user\t\t= mysql/' \ 46 | --expression='s,^tmpdir\t\t= /tmp,tmpdir\t\t= /var/tmp,' \ 47 | --expression='/\[mysqld]/ a\ secure-file-priv = ""\' \ 48 | /etc/mysql/my.cnf 49 | # patch mysql conf to use smaller transaction logs to save disk space 50 | cat < /etc/mysql/conf.d/sandstorm.cnf 51 | [mysqld] 52 | # Set the transaction log file to the minimum allowed size to save disk space. 53 | innodb_log_file_size = 1048576 54 | # Set the main data file to grow by 1MB at a time, rather than 8MB at a time. 55 | innodb_autoextend_increment = 1 56 | EOF 57 | -------------------------------------------------------------------------------- /stacks/lesp/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Checks if there's a composer.json, and if so, installs/runs composer. 3 | 4 | set -euo pipefail 5 | 6 | cd /opt/app 7 | 8 | if [ -f /opt/app/composer.json ] ; then 9 | if [ ! -f composer.phar ] ; then 10 | curl -sS https://getcomposer.org/installer | php 11 | fi 12 | php composer.phar install 13 | fi 14 | -------------------------------------------------------------------------------- /stacks/lesp/launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | wait_for() { 6 | local service=$1 7 | local file=$2 8 | while [ ! -e "$file" ] ; do 9 | echo "waiting for $service to be available at $file." 10 | sleep .1 11 | done 12 | } 13 | 14 | # Create a bunch of folders under the clean /var that php and nginx expect to exist 15 | mkdir -p /var/lib/nginx 16 | mkdir -p /var/lib/php/sessions 17 | mkdir -p /var/log 18 | mkdir -p /var/log/nginx 19 | # Wipe /var/run, since pidfiles and socket files from previous launches should go away 20 | # TODO someday: I'd prefer a tmpfs for these. 21 | rm -rf /var/run 22 | mkdir -p /var/run/php 23 | 24 | # Rotate log files larger than 512K 25 | log_files="$(find /var/log -type f -name '*.log')" 26 | for f in $log_files; do 27 | if [ $(du -b "$f" | awk '{print $1}') -ge $((512 * 1024)) ] ; then 28 | mv $f $f.1 29 | fi 30 | done 31 | 32 | # Spawn php 33 | /usr/sbin/php-fpm8.2 --nodaemonize --fpm-config /etc/php/8.2/fpm/php-fpm.conf & 34 | # Wait until php has bound its socket, indicating readiness 35 | wait_for php-fpm8.2 /var/run/php/php8.2-fpm.sock 36 | 37 | # Start nginx. 38 | /usr/sbin/nginx -c /opt/app/.sandstorm/service-config/nginx.conf -g "daemon off;" 39 | -------------------------------------------------------------------------------- /stacks/lesp/service-config/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /stacks/lesp/service-config/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 4; 2 | pid /var/run/nginx.pid; 3 | 4 | events { 5 | worker_connections 768; 6 | # multi_accept on; 7 | } 8 | 9 | http { 10 | # Basic Settings 11 | sendfile on; 12 | tcp_nopush on; 13 | tcp_nodelay on; 14 | keepalive_timeout 65; 15 | types_hash_max_size 2048; 16 | # server_names_hash_bucket_size 64; 17 | server_tokens off; 18 | server_name_in_redirect off; 19 | 20 | include mime.types; 21 | default_type application/octet-stream; 22 | 23 | # Logging 24 | access_log off; 25 | error_log stderr; 26 | 27 | # Prevent nginx from adding compression; this interacts badly with Sandstorm 28 | # WebSession due to https://github.com/sandstorm-io/sandstorm/issues/289 29 | gzip off; 30 | 31 | # Trust the sandstorm-http-bridge's X-Forwarded-Proto. 32 | map $http_x_forwarded_proto $fe_https { 33 | default ""; 34 | https on; 35 | } 36 | 37 | server { 38 | listen 8000 default_server; 39 | listen [::]:8000 default_server ipv6only=on; 40 | 41 | # Allow arbitrarily large bodies - Sandstorm can handle them, and requests 42 | # are authenticated already, so there's no reason for apps to add additional 43 | # limits by default. 44 | client_max_body_size 0; 45 | 46 | server_name localhost; 47 | root /opt/app; 48 | location / { 49 | index index.php; 50 | try_files $uri $uri/ =404; 51 | } 52 | location ~ \.php$ { 53 | fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; 54 | fastcgi_index index.php; 55 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 56 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 57 | fastcgi_param QUERY_STRING $query_string; 58 | fastcgi_param REQUEST_METHOD $request_method; 59 | fastcgi_param CONTENT_TYPE $content_type; 60 | fastcgi_param CONTENT_LENGTH $content_length; 61 | 62 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 63 | fastcgi_param REQUEST_URI $request_uri; 64 | fastcgi_param DOCUMENT_URI $document_uri; 65 | fastcgi_param DOCUMENT_ROOT $document_root; 66 | fastcgi_param SERVER_PROTOCOL $server_protocol; 67 | fastcgi_param HTTPS $fe_https if_not_empty; 68 | 69 | fastcgi_param GATEWAY_INTERFACE CGI/1.1; 70 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; 71 | 72 | fastcgi_param REMOTE_ADDR $remote_addr; 73 | fastcgi_param REMOTE_PORT $remote_port; 74 | fastcgi_param SERVER_ADDR $server_addr; 75 | fastcgi_param SERVER_PORT $server_port; 76 | fastcgi_param SERVER_NAME $server_name; 77 | 78 | # PHP only, required if PHP was built with --enable-force-cgi-redirect 79 | fastcgi_param REDIRECT_STATUS 200; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /stacks/lesp/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # When you change this file, you must take manual action. Read this doc: 4 | # - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh 5 | 6 | set -euo pipefail 7 | 8 | export DEBIAN_FRONTEND=noninteractive 9 | apt-get update 10 | apt-get install -y nginx php-fpm php-sqlite3 php-cli php-curl git php-dev 11 | service nginx stop 12 | service php8.2-fpm stop 13 | systemctl disable nginx 14 | systemctl disable php8.2-fpm 15 | # patch /etc/php/8.2/fpm/pool.d/www.conf to not change uid/gid to www-data 16 | sed --in-place='' \ 17 | --expression='s/^listen.owner = www-data/;listen.owner = www-data/' \ 18 | --expression='s/^listen.group = www-data/;listen.group = www-data/' \ 19 | --expression='s/^user = www-data/;user = www-data/' \ 20 | --expression='s/^group = www-data/;group = www-data/' \ 21 | /etc/php/8.2/fpm/pool.d/www.conf 22 | # patch /etc/php/8.2/fpm/php-fpm.conf to not have a pidfile 23 | sed --in-place='' \ 24 | --expression='s/^pid =/;pid =/' \ 25 | /etc/php/8.2/fpm/php-fpm.conf 26 | # patch /etc/php/8.2/fpm/php-fpm.conf to place the sock file in /var 27 | sed --in-place='' \ 28 | --expression='s/^listen = \/run\/php\/php8.2-fpm.sock/listen = \/var\/run\/php\/php8.2-fpm.sock/' \ 29 | /etc/php/8.2/fpm/pool.d/www.conf 30 | # patch /etc/php/8.2/fpm/pool.d/www.conf to no clear environment variables 31 | # so we can pass in SANDSTORM=1 to apps 32 | sed --in-place='' \ 33 | --expression='s/^;clear_env = no/clear_env=no/' \ 34 | /etc/php/8.2/fpm/pool.d/www.conf 35 | -------------------------------------------------------------------------------- /stacks/meteor/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # Make meteor bundle 5 | 6 | export NODE_ENV=production 7 | sudo chown vagrant:vagrant /home/vagrant -R 8 | 9 | cd /opt/app 10 | meteor npm install --production 11 | meteor build --directory /home/vagrant/ 12 | # Use npm and node from the Meteor dev bundle to install the bundle's dependencies. 13 | TOOL_VERSION=$(meteor show --ejson $(<.meteor/release) | grep '^ *"tool":' | 14 | sed -re 's/^.*"(meteor-tool@[^"]*)".*$/\1/g') 15 | TOOLDIR=$(echo $TOOL_VERSION | tr @ /) 16 | PATH=$HOME/.meteor/packages/$TOOLDIR/mt-os.linux.x86_64/dev_bundle/bin:$PATH 17 | cd /home/vagrant/bundle/programs/server 18 | npm install --production 19 | 20 | # Copy our launcher script into the bundle so the grain can start up. 21 | mkdir -p /home/vagrant/bundle/opt/app/.sandstorm/ 22 | cp /opt/app/.sandstorm/launcher.sh /home/vagrant/bundle/opt/app/.sandstorm/ 23 | -------------------------------------------------------------------------------- /stacks/meteor/initargs: -------------------------------------------------------------------------------- 1 | -I /home/vagrant/bundle -I /opt/meteor-spk/meteor-spk.deps -A 2 | -------------------------------------------------------------------------------- /stacks/meteor/launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | exec node /start.js -p 8000 5 | -------------------------------------------------------------------------------- /stacks/meteor/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # When you change this file, you must take manual action. Read this doc: 4 | # - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh 5 | 6 | set -euo pipefail 7 | 8 | CURL_OPTS="--silent --show-error" 9 | apt-get update 10 | apt-get install -y build-essential git 11 | 12 | cd /opt/ 13 | 14 | NODE_ENV=production 15 | PACKAGE=meteor-spk-0.5.1 16 | PACKAGE_FILENAME="$PACKAGE.tar.xz" 17 | CACHE_TARGET="/host-dot-sandstorm/caches/${PACKAGE_FILENAME}" 18 | 19 | # Fetch meteor-spk tarball if not cached 20 | if [ ! -f "$CACHE_TARGET" ] ; then 21 | echo -n "Downloading ${PACKAGE}..." 22 | curl $CURL_OPTS https://dl.sandstorm.io/${PACKAGE_FILENAME} > "$CACHE_TARGET.partial" 23 | mv "${CACHE_TARGET}.partial" "${CACHE_TARGET}" 24 | echo "...done." 25 | fi 26 | 27 | # Extract to /opt 28 | tar xf "$CACHE_TARGET" 29 | 30 | # Create symlink so we can rely on the path /opt/meteor-spk 31 | if [ ! -e meteor-spk ] ; then ln -s "${PACKAGE}" meteor-spk ; fi 32 | 33 | # Add bash, and its dependencies, so they get mapped into the image. 34 | # Bash runs the launcher script. 35 | cp -a /bin/bash /opt/meteor-spk/meteor-spk.deps/bin/ 36 | cp -a /lib/x86_64-linux-gnu/libncurses.so.* /opt/meteor-spk/meteor-spk.deps/lib/x86_64-linux-gnu/ 37 | cp -a /lib/x86_64-linux-gnu/libtinfo.so.* /opt/meteor-spk/meteor-spk.deps/lib/x86_64-linux-gnu/ 38 | 39 | # Unfortunately, Meteor does not explicitly make it easy to cache packages, but 40 | # we know experimentally that the package is mostly directly extractable to a 41 | # user's $HOME/.meteor directory. 42 | METEOR_RELEASE=1.10.1 43 | METEOR_PLATFORM=os.linux.x86_64 44 | METEOR_TARBALL_FILENAME="meteor-bootstrap-${METEOR_PLATFORM}.tar.gz" 45 | METEOR_TARBALL_URL="https://d3sqy0vbqsdhku.cloudfront.net/packages-bootstrap/${METEOR_RELEASE}/${METEOR_TARBALL_FILENAME}" 46 | METEOR_CACHE_TARGET="/host-dot-sandstorm/caches/${METEOR_TARBALL_FILENAME}" 47 | 48 | # Fetch meteor tarball if not cached 49 | if [ ! -f "$METEOR_CACHE_TARGET" ] ; then 50 | echo -n "Downloading Meteor version ${METEOR_RELEASE}..." 51 | curl $CURL_OPTS "$METEOR_TARBALL_URL" > "${METEOR_CACHE_TARGET}.partial" 52 | mv "${METEOR_CACHE_TARGET}"{.partial,} 53 | echo "...done." 54 | fi 55 | 56 | # Extract as unprivileged user, which is the usual meteor setup 57 | cd /home/vagrant/ 58 | su -c "tar xf '${METEOR_CACHE_TARGET}'" vagrant 59 | # Link into global PATH 60 | if [ ! -e /usr/bin/meteor ] ; then ln -s /home/vagrant/.meteor/meteor /usr/bin/meteor ; fi 61 | chown vagrant:vagrant /home/vagrant -R 62 | -------------------------------------------------------------------------------- /stacks/node/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | # This script is run in the VM each time you run `vagrant-spk dev`. This is 4 | # the ideal place to invoke anything which is normally part of your app's build 5 | # process - transforming the code in your repository into the collection of files 6 | # which can actually run the service in production 7 | # 8 | # Some examples: 9 | # 10 | # * For a C/C++ application, calling 11 | # ./configure && make && make install 12 | # * For a Python application, creating a virtualenv and installing 13 | # app-specific package dependencies: 14 | # virtualenv /opt/app/env 15 | # /opt/app/env/bin/pip install -r /opt/app/requirements.txt 16 | # * Building static assets from .less or .sass, or bundle and minify JS 17 | # * Collecting various build artifacts or assets into a deployment-ready 18 | # directory structure 19 | 20 | # By default, this script does nothing. You'll have to modify it as 21 | # appropriate for your application. 22 | cd /opt/app 23 | 24 | npm install 25 | 26 | # bower install 27 | -------------------------------------------------------------------------------- /stacks/node/launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | # This script is run every time an instance of our app - aka grain - starts up. 4 | # This is the entry point for your application both when a grain is first launched 5 | # and when a grain resumes after being previously shut down. 6 | # 7 | # This script is responsible for launching everything your app needs to run. The 8 | # thing it should do *last* is: 9 | # 10 | # * Start a process in the foreground listening on port 8000 for HTTP requests. 11 | # 12 | # This is how you indicate to the platform that your application is up and 13 | # ready to receive requests. Often, this will be something like nginx serving 14 | # static files and reverse proxying for some other dynamic backend service. 15 | # 16 | # Other things you probably want to do in this script include: 17 | # 18 | # * Building folder structures in /var. /var is the only non-tmpfs folder 19 | # mounted read-write in the sandbox, and when a grain is first launched, it 20 | # will start out empty. It will persist between runs of the same grain, but 21 | # be unique per app instance. That is, two instances of the same app have 22 | # separate instances of /var. 23 | # * Preparing a database and running migrations. As your package changes 24 | # over time and you release updates, you will need to deal with migrating 25 | # data from previous schema versions to new ones, since users should not have 26 | # to think about such things. 27 | # * Launching other daemons your app needs (e.g. mysqld, redis-server, etc.) 28 | 29 | # By default, this script does nothing. You'll have to modify it as 30 | # appropriate for your application. 31 | cd /opt/app 32 | 33 | # express.js has fewer problems when HOME is defined 34 | export HOME=/opt/app 35 | 36 | npm start 37 | -------------------------------------------------------------------------------- /stacks/node/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # When you change this file, you must take manual action. Read this doc: 4 | # - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh 5 | 6 | set -euo pipefail 7 | # Install node.js 8 | 9 | # Discussion, issues and change requests at: 10 | # https://github.com/nodesource/distributions 11 | # 12 | # Script to install the NodeSource Node.js 10.x repo onto a 13 | # Debian or Ubuntu system. 14 | 15 | export DEBIAN_FRONTEND=noninteractive 16 | 17 | mkdir -p /etc/apt/keyrings 18 | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg 19 | NODE_MAJOR=20 20 | echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list 21 | apt-get update 22 | 23 | # Actually install node 24 | apt-get install -qq nodejs git-core g++ 25 | 26 | exit 0 27 | -------------------------------------------------------------------------------- /stacks/static/launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | mkdir -p /var/lib/nginx 5 | mkdir -p /var/log/nginx 6 | # Wipe /var/run, since pidfiles and socket files from previous launches should go away 7 | # TODO someday: I'd prefer a tmpfs for these. 8 | rm -rf /var/run 9 | mkdir -p /var/run 10 | 11 | # Rotate log files larger than 512K 12 | log_files="$(find /var/log -type f -name '*.log')" 13 | for f in $log_files; do 14 | if [ $(du -b "$f" | awk '{print $1}') -ge $((512 * 1024)) ] ; then 15 | mv $f $f.1 16 | fi 17 | done 18 | 19 | # Start nginx. 20 | /usr/sbin/nginx -c /opt/app/.sandstorm/service-config/nginx.conf -g "daemon off;" 21 | -------------------------------------------------------------------------------- /stacks/static/service-config/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /stacks/static/service-config/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 4; 2 | pid /var/run/nginx.pid; 3 | 4 | events { 5 | worker_connections 768; 6 | # multi_accept on; 7 | } 8 | 9 | http { 10 | # Basic Settings 11 | sendfile on; 12 | tcp_nopush on; 13 | tcp_nodelay on; 14 | keepalive_timeout 65; 15 | types_hash_max_size 2048; 16 | # server_names_hash_bucket_size 64; 17 | server_tokens off; 18 | server_name_in_redirect off; 19 | 20 | include mime.types; 21 | default_type application/octet-stream; 22 | 23 | # Logging 24 | access_log off; 25 | error_log stderr; 26 | 27 | # Prevent nginx from adding compression; this interacts badly with Sandstorm 28 | # WebSession due to https://github.com/sandstorm-io/sandstorm/issues/289 29 | gzip off; 30 | 31 | server { 32 | listen 8000 default_server; 33 | listen [::]:8000 default_server ipv6only=on; 34 | 35 | # Allow arbitrarily large bodies - Sandstorm can handle them, and requests 36 | # are authenticated already, so there's no reason for apps to add additional 37 | # limits by default. 38 | client_max_body_size 0; 39 | 40 | server_name localhost; 41 | root /opt/app; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /stacks/static/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # When you change this file, you must take manual action. Read this doc: 4 | # - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh 5 | 6 | set -euo pipefail 7 | 8 | export DEBIAN_FRONTEND=noninteractive 9 | apt-get update 10 | apt-get install -y nginx 11 | service nginx stop 12 | systemctl disable nginx 13 | -------------------------------------------------------------------------------- /stacks/uwsgi/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | VENV=/opt/app-venv 4 | if [ ! -d $VENV ] ; then 5 | sudo mkdir -p $VENV -m777 6 | virtualenv $VENV 7 | else 8 | echo "$VENV exists, moving on" 9 | fi 10 | 11 | if [ -f /opt/app/requirements.txt ] ; then 12 | export MYSQLCLIENT_LDFLAGS=$(pkg-config --libs mysqlclient) 13 | export MYSQLCLIENT_CFLAGS=$(pkg-config --cflags mysqlclient) 14 | $VENV/bin/pip install -r /opt/app/requirements.txt 15 | fi 16 | -------------------------------------------------------------------------------- /stacks/uwsgi/launcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | wait_for() { 6 | local service=$1 7 | local file=$2 8 | while [ ! -e "$file" ] ; do 9 | echo "waiting for $service to be available at $file." 10 | sleep .1 11 | done 12 | } 13 | 14 | # something something folders 15 | mkdir -p /var/lib/mysql 16 | mkdir -p /var/lib/mysql-files 17 | mkdir -p /var/lib/nginx 18 | mkdir -p /var/log 19 | mkdir -p /var/log/mysql 20 | mkdir -p /var/log/nginx 21 | # Wipe /var/run, since pidfiles and socket files from previous launches should go away 22 | # TODO someday: I'd prefer a tmpfs for these. 23 | rm -rf /var/run 24 | mkdir -p /var/run 25 | rm -rf /var/tmp 26 | mkdir -p /var/tmp 27 | mkdir -p /var/run/mysqld 28 | 29 | # Rotate log files larger than 512K 30 | log_files="$(find /var/log -type f -name '*.log')" 31 | for f in $log_files; do 32 | if [ $(du -b "$f" | awk '{print $1}') -ge $((512 * 1024)) ] ; then 33 | mv $f $f.1 34 | fi 35 | done 36 | 37 | UWSGI_SOCKET_FILE=/var/run/uwsgi.sock 38 | 39 | if [ ! -d /var/lib/mysql/mysql ]; then 40 | # Ensure mysql tables created 41 | HOME=/etc/mysql /usr/sbin/mysqld --initialize 42 | fi 43 | 44 | # Spawn mysqld 45 | HOME=/etc/mysql /usr/sbin/mysqld --skip-grant-tables & 46 | 47 | MYSQL_SOCKET_FILE=/var/run/mysqld/mysqld.sock 48 | # Wait for mysql to bind its socket 49 | wait_for mysql $MYSQL_SOCKET_FILE 50 | 51 | # # Uncomment this block if you need to preload a database for your app 52 | # if [ ! -e /var/.db-created ]; then 53 | # mysql --user root -e 'CREATE DATABASE app' 54 | # mysql --user root --database app < /opt/app/install.sql 55 | # touch /var/.db-created 56 | # fi 57 | 58 | # Spawn uwsgi 59 | HOME=/var uwsgi \ 60 | --socket $UWSGI_SOCKET_FILE \ 61 | --plugin python3 \ 62 | --virtualenv /opt/app-venv \ 63 | --wsgi-file /opt/app/main.py & 64 | 65 | # Wait for uwsgi to bind its socket 66 | wait_for uwsgi $UWSGI_SOCKET_FILE 67 | 68 | # Start nginx. 69 | /usr/sbin/nginx -c /opt/app/.sandstorm/service-config/nginx.conf -g "daemon off;" 70 | 71 | -------------------------------------------------------------------------------- /stacks/uwsgi/service-config/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /stacks/uwsgi/service-config/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 4; 2 | pid /var/run/nginx.pid; 3 | 4 | events { 5 | worker_connections 768; 6 | # multi_accept on; 7 | } 8 | 9 | http { 10 | # Basic Settings 11 | sendfile on; 12 | tcp_nopush on; 13 | tcp_nodelay on; 14 | keepalive_timeout 65; 15 | types_hash_max_size 2048; 16 | # server_names_hash_bucket_size 64; 17 | server_tokens off; 18 | server_name_in_redirect off; 19 | 20 | include mime.types; 21 | default_type application/octet-stream; 22 | 23 | # Logging 24 | access_log off; 25 | error_log stderr; 26 | 27 | # Prevent nginx from adding compression; this interacts badly with Sandstorm 28 | # WebSession due to https://github.com/sandstorm-io/sandstorm/issues/289 29 | gzip off; 30 | 31 | # Trust the sandstorm-http-bridge's X-Forwarded-Proto. 32 | map $http_x_forwarded_proto $fe_https { 33 | default ""; 34 | https on; 35 | } 36 | 37 | server { 38 | listen 8000 default_server; 39 | listen [::]:8000 default_server ipv6only=on; 40 | 41 | # Allow arbitrarily large bodies - Sandstorm can handle them, and requests 42 | # are authenticated already, so there's no reason for apps to add additional 43 | # limits by default. 44 | client_max_body_size 0; 45 | 46 | server_name localhost; 47 | root /opt/app; 48 | location /static/ { 49 | alias /opt/app/static/; 50 | } 51 | location / { 52 | uwsgi_pass unix:///var/run/uwsgi.sock; 53 | uwsgi_param QUERY_STRING $query_string; 54 | uwsgi_param REQUEST_METHOD $request_method; 55 | uwsgi_param CONTENT_TYPE $content_type; 56 | uwsgi_param CONTENT_LENGTH $content_length; 57 | 58 | uwsgi_param REQUEST_URI $request_uri; 59 | uwsgi_param PATH_INFO $document_uri; 60 | uwsgi_param DOCUMENT_ROOT $document_root; 61 | uwsgi_param SERVER_PROTOCOL $server_protocol; 62 | uwsgi_param HTTPS $fe_https if_not_empty; 63 | 64 | uwsgi_param REMOTE_ADDR $remote_addr; 65 | uwsgi_param REMOTE_PORT $remote_port; 66 | uwsgi_param SERVER_PORT $server_port; 67 | uwsgi_param SERVER_NAME $server_name; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /stacks/uwsgi/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # When you change this file, you must take manual action. Read this doc: 4 | # - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh 5 | 6 | set -euo pipefail 7 | 8 | export DEBIAN_FRONTEND=noninteractive 9 | 10 | echo -e "deb http://repo.mysql.com/apt/debian/ bookworm mysql-8.0\ndeb-src http://repo.mysql.com/apt/debian/ bookworm mysql-8.0" > /etc/apt/sources.list.d/mysql.list 11 | wget -O /tmp/RPM-GPG-KEY-mysql https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 12 | apt-key add /tmp/RPM-GPG-KEY-mysql 13 | 14 | apt-get update 15 | apt-get install -y nginx mysql-server libmysqlclient-dev libssl-dev pkg-config uwsgi uwsgi-plugin-python3 build-essential python3-dev python3-mysqldb python3-virtualenv git 16 | # patch mysql conf to not change uid, and to use /var/tmp over /tmp 17 | sed --in-place='' \ 18 | --expression='s/^user\t\t= mysql/#user\t\t= mysql/' \ 19 | --expression='s,^tmpdir\t\t= /tmp,tmpdir\t\t= /var/tmp,' \ 20 | /etc/mysql/my.cnf 21 | # patch mysql conf to use smaller transaction logs to save disk space 22 | cat < /etc/mysql/conf.d/sandstorm.cnf 23 | [mysqld] 24 | # Set the transaction log file to the minimum allowed size to save disk space. 25 | innodb_log_file_size = 1048576 26 | # Set the main data file to grow by 1MB at a time, rather than 8MB at a time. 27 | innodb_autoextend_increment = 1 28 | EOF 29 | service nginx stop 30 | service mysql stop 31 | systemctl disable nginx 32 | systemctl disable mysql 33 | -------------------------------------------------------------------------------- /vagrant-spk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright 2015-2018 Sandstorm Development Group, Inc. and contributors 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Example usage: 17 | # vagrant-spk setupvm lemp 18 | # vagrant-spk vm up 19 | # vagrant-spk init 20 | # vagrant-spk dev 21 | # vagrant-spk pack output.spk 22 | 23 | from __future__ import print_function 24 | 25 | # TODO(someday): Increment version number and commit as part of release.sh 26 | __version__ = "v1.2" 27 | 28 | import argparse 29 | import os 30 | import glob 31 | import hashlib 32 | import re 33 | import shutil 34 | import subprocess 35 | import sys 36 | 37 | PWD = os.getcwd() 38 | ENABLE_EXPERIMENTAL = False 39 | 40 | EXECUTABLE_PATH = os.path.realpath(sys.argv[0]) 41 | 42 | # When run as bundled by pyinstaller, sys.argv[0] is simply "vagrant-spk". 43 | # os.path.realpath("vagrant-spk") will produce a path in the current working directory, which 44 | # probably doesn't actually exist, and definitely doesn't contain the "stacks" folder that we 45 | # expect to find in the same folder as the executable. So in that environment, use 46 | # sys.executable to find the folder the program is running from. 47 | # When run not bundled, sys.executable is something like /usr/bin/python, so only try this 48 | # if sys.argv[0] doesn't appear to be an actual living file. 49 | if not os.path.exists(EXECUTABLE_PATH): 50 | EXECUTABLE_PATH = sys.executable 51 | CODE_DIR = os.path.dirname(EXECUTABLE_PATH) 52 | 53 | VAGRANTFILE_CONTENTS = r"""# -*- mode: ruby -*- 54 | # vi: set ft=ruby : 55 | 56 | # CAUTION: DO NOT MAKE CHANGES TO THIS FILE. The vagrant-spk upgradevm process will overwrite it. 57 | 58 | # Guess at a reasonable name for the VM based on the folder vagrant-spk is 59 | # run from. The timestamp is there to avoid conflicts if you have multiple 60 | # folders with the same name. 61 | VM_NAME = File.basename(File.dirname(File.dirname(__FILE__))) + "_sandstorm_#{Time.now.utc.to_i}" 62 | 63 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 64 | VAGRANTFILE_API_VERSION = "2" 65 | 66 | # ugly hack to prevent hashicorp's bitrot. See https://github.com/hashicorp/vagrant/issues/9442 67 | # this setting is required for pre-2.0 vagrant, but causes an error as of 2.0.3, 68 | # remove entirely when confident nobody uses vagrant 1.x for anything. 69 | unless Vagrant::DEFAULT_SERVER_URL.frozen? 70 | Vagrant::DEFAULT_SERVER_URL.replace('https://vagrantcloud.com') 71 | end 72 | 73 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 74 | # Base on a 64-bit Debian box with vboxsf support (ex. contrib-buster64, bullseye64) 75 | config.vm.box = "debian/bookworm64" 76 | config.vm.post_up_message = "Your virtual server is running at: http://local.sandstorm.io:6090" 77 | 78 | 79 | if Vagrant.has_plugin?("vagrant-vbguest") then 80 | # vagrant-vbguest is a Vagrant plugin that upgrades 81 | # the version of VirtualBox Guest Additions within each 82 | # guest. If you have the vagrant-vbguest plugin, then it 83 | # needs to know how to compile kernel modules, etc., and so 84 | # we give it this hint about operating system type. 85 | config.vm.guest = "debian" 86 | config.vbguest.auto_update = false 87 | end 88 | 89 | # We forward port 6090, the vagrant-spk web port, so that developers can 90 | # visit their Sandstorm app from their browser as local.sandstorm.io:6090 91 | # (aka 127.0.0.1:6090). 92 | config.vm.network :forwarded_port, guest: 6090, host: 6090, host_ip: "127.0.0.1" 93 | 94 | # Use a shell script to "provision" the box. This installs Sandstorm using 95 | # the bundled installer. 96 | config.vm.provision "shell", inline: "sudo bash /opt/app/.sandstorm/global-setup.sh", keep_color: true 97 | # Then, do stack-specific and app-specific setup. 98 | config.vm.provision "shell", inline: "sudo bash /opt/app/.sandstorm/setup.sh", keep_color: true 99 | 100 | # Shared folders are configured per-provider since vboxsf can't handle >4096 open files, 101 | # NFS requires privilege escalation every time you bring a VM up, 102 | # and 9p is only available on libvirt. 103 | 104 | # Calculate the number of CPUs and the amount of RAM the system has, 105 | # in a platform-dependent way; further logic below. 106 | cpus = nil 107 | total_kB_ram = nil 108 | 109 | host = RbConfig::CONFIG['host_os'] 110 | if host =~ /darwin/ 111 | cpus = `sysctl -n hw.ncpu`.to_i 112 | total_kB_ram = `sysctl -n hw.memsize`.to_i / 1024 113 | elsif host =~ /linux/ 114 | cpus = `nproc`.to_i 115 | total_kB_ram = `grep MemTotal /proc/meminfo | awk '{print $2}'`.to_i 116 | elsif host =~ /mingw/ 117 | cpus = `powershell -Command "(Get-WmiObject Win32_Processor -Property NumberOfLogicalProcessors | Select-Object -Property NumberOfLogicalProcessors | Measure-Object NumberOfLogicalProcessors -Sum).Sum"`.to_i 118 | total_kB_ram = `powershell -Command "[math]::Round((Get-WmiObject -Class Win32_ComputerSystem).TotalPhysicalMemory)"`.to_i / 1024 119 | end 120 | # Use the same number of CPUs within Vagrant as the system, with 1 121 | # as a default. 122 | # 123 | # Use at least 512MB of RAM, and if the system has more than 2GB of 124 | # RAM, use 1/4 of the system RAM. This seems a reasonable compromise 125 | # between having the Vagrant guest operating system not run out of 126 | # RAM entirely (which it basically would if we went much lower than 127 | # 512MB) and also allowing it to use up a healthily large amount of 128 | # RAM so it can run faster on systems that can afford it. 129 | if cpus.nil? or cpus.zero? 130 | cpus = 1 131 | end 132 | if total_kB_ram.nil? or total_kB_ram < 2048000 133 | assign_ram_mb = 512 134 | else 135 | assign_ram_mb = (total_kB_ram / 1024 / 4) 136 | end 137 | # Actually apply these CPU/memory values to the providers. 138 | config.vm.provider :virtualbox do |vb, override| 139 | vb.cpus = cpus 140 | vb.memory = assign_ram_mb 141 | vb.name = VM_NAME 142 | vb.customize ["modifyvm", :id, "--nictype1", "Am79C973"] 143 | 144 | # /opt/app and /host-dot-sandstorm are used by vagrant-spk 145 | override.vm.synced_folder "..", "/opt/app" 146 | override.vm.synced_folder ENV["HOME"] + "/.sandstorm", "/host-dot-sandstorm", mount_options: ["x-systemd.automount"] 147 | # /vagrant is not used by vagrant-spk; we need this line so it gets disabled; if we removed the 148 | # line, vagrant would automatically insert a synced folder in /vagrant, which is not what we want. 149 | override.vm.synced_folder "..", "/vagrant", disabled: true 150 | end 151 | config.vm.provider :libvirt do |libvirt, override| 152 | libvirt.cpus = cpus 153 | libvirt.memory = assign_ram_mb 154 | libvirt.default_prefix = VM_NAME 155 | 156 | # /opt/app and /host-dot-sandstorm are used by vagrant-spk 157 | override.vm.synced_folder "..", "/opt/app", type: "9p", accessmode: "passthrough" 158 | override.vm.synced_folder ENV["HOME"] + "/.sandstorm", "/host-dot-sandstorm", type: "9p", accessmode: "passthrough" 159 | # /vagrant is not used by vagrant-spk; we need this line so it gets disabled; if we removed the 160 | # line, vagrant would automatically insert a synced folder in /vagrant, which is not what we want. 161 | override.vm.synced_folder "..", "/vagrant", type: "9p", accessmode: "passthrough", disabled: true 162 | end 163 | end 164 | """ 165 | 166 | GITATTRIBUTES_CONTENTS = r""" 167 | 168 | # vagrant-spk creates shell scripts, which must end in \n, even on a \r\n system. 169 | *.sh text eol=lf 170 | 171 | """ 172 | 173 | GITIGNORE_CONTENTS = r""" 174 | 175 | # This file stores a list of sub-paths of .sandstorm/ that should be ignored by git. 176 | .vagrant 177 | 178 | # Vagrant sometimes generates log files: 179 | *.log 180 | 181 | """ 182 | 183 | GLOBAL_SETUP_SCRIPT = r"""#!/bin/bash 184 | set -euo pipefail 185 | 186 | # CAUTION: DO NOT MAKE CHANGES TO THIS FILE. The vagrant-spk upgradevm process will overwrite it. 187 | # App-specific setup should be done in the setup.sh file. 188 | 189 | # Set options for curl. Since we only want to show errors from these curl commands, we also use 190 | # 'cat' to buffer the output; for more information: 191 | # https://github.com/sandstorm-io/vagrant-spk/issues/158 192 | 193 | CURL_OPTS="--silent --show-error" 194 | echo localhost > /etc/hostname 195 | hostname localhost 196 | 197 | # Grub updates don't silent install well 198 | apt-mark hold grub-pc 199 | apt-get update 200 | apt-get upgrade -y 201 | 202 | # Install curl needed below, and gnupg for package signing 203 | apt-get install -y curl gnupg 204 | 205 | # The following line copies stderr through stderr to cat without accidentally leaving it in the 206 | # output file. Be careful when changing. See: https://github.com/sandstorm-io/vagrant-spk/pull/159 207 | curl $CURL_OPTS https://install.sandstorm.io/ 2>&1 > /host-dot-sandstorm/caches/install.sh | cat 208 | 209 | SANDSTORM_CURRENT_VERSION=$(curl $CURL_OPTS -f "https://install.sandstorm.io/dev?from=0&type=install") 210 | SANDSTORM_PACKAGE="sandstorm-$SANDSTORM_CURRENT_VERSION.tar.xz" 211 | if [[ ! -f /host-dot-sandstorm/caches/$SANDSTORM_PACKAGE ]] ; then 212 | echo -n "Downloading Sandstorm version ${SANDSTORM_CURRENT_VERSION}..." 213 | curl $CURL_OPTS --output "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE.partial" "https://dl.sandstorm.io/$SANDSTORM_PACKAGE" 2>&1 | cat 214 | mv "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE.partial" "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE" 215 | echo "...done." 216 | fi 217 | if [ ! -e /opt/sandstorm/latest/sandstorm ] ; then 218 | echo -n "Installing Sandstorm version ${SANDSTORM_CURRENT_VERSION}..." 219 | bash /host-dot-sandstorm/caches/install.sh -d -e -p 6090 "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE" >/dev/null 220 | echo "...done." 221 | fi 222 | modprobe ip_tables 223 | # Make the vagrant user part of the sandstorm group so that commands like 224 | # `spk dev` work. 225 | usermod -a -G 'sandstorm' 'vagrant' 226 | # Bind to all addresses, so the vagrant port-forward works. 227 | sudo sed --in-place='' \ 228 | --expression='s/^BIND_IP=.*/BIND_IP=0.0.0.0/' \ 229 | /opt/sandstorm/sandstorm.conf 230 | 231 | # Force vagrant-spk to use the strict CSP, see sandstorm#3424 for details. 232 | echo 'ALLOW_LEGACY_RELAXED_CSP=false' >> /opt/sandstorm/sandstorm.conf 233 | 234 | sudo service sandstorm restart 235 | # Enable apt-cacher-ng proxy to make things faster if one appears to be running on the gateway IP 236 | GATEWAY_IP=$(ip route | grep ^default | cut -d ' ' -f 3) 237 | if nc -z "$GATEWAY_IP" 3142 ; then 238 | echo "Acquire::http::Proxy \"http://$GATEWAY_IP:3142\";" > /etc/apt/apt.conf.d/80httpproxy 239 | fi 240 | # Configure apt to retry fetching things that fail to download. 241 | echo "APT::Acquire::Retries \"10\";" > /etc/apt/apt.conf.d/80sandstorm-retry 242 | """ 243 | 244 | EMPTY_BUILD_SCRIPT = r"""#!/bin/bash 245 | set -euo pipefail 246 | # This is a script that is run every time you call "vagrant-spk dev". 247 | # It is intended to do platform-and-repository-specific build steps. You 248 | # might customize it to do any of the following, or more: 249 | # - for Python, prepare a virtualenv and pip install -r requirements.txt 250 | # - for PHP, call composer to retrieve additional packages 251 | # - for JS/CSS/SASS/LESS, compile, minify, or otherwise do asset pipeline work. 252 | # This particular script does nothing at present, but you should adapt it 253 | # sensibly for your package. 254 | 255 | exit 0 256 | """ 257 | 258 | # Where to look for the vagrant-spk dir from inside the vm 259 | TMP_VAGRANT_SPK_LOCATION = '/var/tmp/vagrant-spk' 260 | 261 | def format_shell_grain_choices(supervisors): 262 | '''Return a formatted message based on the list of supervisor PIDs that were found, asking which 263 | grain the user wants to attach a shell to.''' 264 | 265 | assert supervisors # caller is supposed to bail out for us if this is empty 266 | msg = ( 267 | '''This will run a shell in the context of a grain. Here is a list of running 268 | grains you can attach to. Press enter to choose the first one, or type the 269 | number next to the grain ID to choose it.''' + '\n\n') 270 | for i, datum in enumerate(supervisors): 271 | human_printable_i = i + 1 272 | msg += "{}. {}\n".format( 273 | human_printable_i, datum['grain_id']) 274 | msg += '\n' 275 | msg += 'Your choice: [1] ' 276 | return msg 277 | 278 | def check_dot_sandstorm(): 279 | expected_path = os.path.join(PWD, ".sandstorm") 280 | if not os.path.isdir(expected_path): 281 | raise Exception("expected to find a .sandstorm folder at {}".format(os.path.abspath(expected_path))) 282 | expected_vagrantfile_path = os.path.join(expected_path, "Vagrantfile") 283 | if not os.path.exists(expected_vagrantfile_path): 284 | raise Exception("expected a Vagrantfile at {} - try 'vagrant-spk setupvm lemp' to generate one".format(expected_vagrantfile_path)) 285 | 286 | def call_vagrant_ssh_command_capturing_output(sandstorm_dir, 287 | vagrant_ssh_command_string, 288 | assert_exit_success=True): 289 | argv = ['vagrant', 'ssh', '-c', vagrant_ssh_command_string] 290 | p = subprocess.Popen(argv, 291 | stdout=subprocess.PIPE, 292 | stdin=subprocess.PIPE, 293 | cwd=sandstorm_dir) 294 | out, err = p.communicate() 295 | # Ignore stderr, since it contains the vagrant output line of "Connection closed" 296 | # and our commands work via stdout anyway. 297 | if assert_exit_success: 298 | if p.returncode != 0: 299 | raise ValueError("Yikes, the command crashed. Command was: %s", 300 | vagrant_ssh_command_string) 301 | return out 302 | 303 | 304 | def call_vagrant_ssh_command_providing_stdin(sandstorm_dir, 305 | vagrant_ssh_command_string, 306 | stdin, 307 | assert_exit_success=True): 308 | p = subprocess.Popen(['vagrant', 'ssh', '-c', vagrant_ssh_command_string], 309 | stdout=subprocess.PIPE, 310 | stdin=subprocess.PIPE, 311 | cwd=sandstorm_dir) 312 | out, err = p.communicate(input=stdin) 313 | # Ignore stderr, since it contains the vagrant output line of "Connection closed" 314 | # and our commands work via stdout anyway. 315 | if assert_exit_success: 316 | if p.returncode != 0: 317 | raise ValueError("Yikes, the command crashed. Command was: %s", 318 | vagrant_ssh_command_string) 319 | return out 320 | 321 | 322 | def call_vagrant_command(sandstorm_dir, *command_args): 323 | command = ["vagrant"] 324 | command.extend(command_args) 325 | sys.stderr.write("Calling {} in {}\n".format(" ".join(["'{}'".format(arg) for arg in command]), sandstorm_dir)) 326 | try: 327 | return subprocess.check_call(command, cwd=sandstorm_dir) 328 | except subprocess.CalledProcessError as e: 329 | ANSI_RED = "\x1b[31m" 330 | ANSI_RESET = "\x1b[0m" 331 | msg = "Command failed with a non-zero exit status (%d)." % e.returncode 332 | sys.exit(ANSI_RED + msg + ANSI_RESET) 333 | 334 | def ensure_working_vboxsf_in_base_box(sandstorm_dir): 335 | # If the .sandstorm/Vagrantfile refers to debian/jessie64 or sandstorm/debian-jessie64 336 | # then we abort the "vagrant-spk up" process because it will not work. 337 | def test_vagrantfile_refers_to_jessie64(): 338 | with open(os.path.join(sandstorm_dir, 'Vagrantfile'), 'r') as fd: 339 | for line in fd: 340 | if line.strip().split() == 'config.vm.box = "debian/jessie64"'.split(): 341 | return True 342 | if line.strip().split() == 'config.vm.box = "sandstorm/debian-jessie64"'.split(): 343 | return True 344 | return False 345 | vagrantfile_refers_to_jessie64 = test_vagrantfile_refers_to_jessie64() 346 | 347 | if vagrantfile_refers_to_jessie64: 348 | msg = ( 349 | """*** INCOMPATIBLE OPTIONS DETECTED *** 350 | 351 | This package was set to use a VM no longer compatible with vagrant-spk. You 352 | can either upgrade to the latest VM supported by vagrant-spk by running 353 | "vagrant-spk upgradevm" or manually update the package to use a compatible 354 | release of Debian Jessie. 355 | 356 | To do this, open .sandstorm/Vagrantfile and find the line beginning with 357 | "config.vm.box" and change the selected box to debian/contrib-jessie64 358 | 359 | For details (including how to bypass this warning) read: 360 | https://github.com/sandstorm-io/vagrant-spk/releases/tag/v0.137 361 | 362 | Once you have done that, please re-run this command ("vagrant-spk up").""") 363 | should_bail = True 364 | 365 | # If the VAGRANT_SPK_IGNORE_WARNINGS environment variable contains "jessie64", then 366 | # proceed anyway. 367 | if ('jessie64' in os.environ.get('VAGRANT_SPK_IGNORE_WARNINGS', '').split()): 368 | msg = '''*** INCOMPATIBLE OPTIONS DETECTED *** 369 | See https://github.com/sandstorm-io/vagrant-spk/releases/tag/v0.136 370 | Proceeding anyway due to VAGRANT_SPK_IGNORE_WARNINGS=jessie64... 371 | ''' 372 | should_bail = False 373 | 374 | print(msg) 375 | if should_bail: 376 | sys.exit(1) 377 | 378 | def ensure_host_sandstorm_folder_exists(): 379 | # We wrap the keyring in an additional folder, because Vagrant shared 380 | # folders can't share single files, only folders, and I don't want to give 381 | # the VM read/write access to the user's entire homedir, just the sandstorm 382 | # keyring. 383 | USER_SANDSTORM_DIR = os.path.join(os.path.expanduser("~"), ".sandstorm") 384 | if not os.path.exists(USER_SANDSTORM_DIR): 385 | print("Creating {} to hold developer keys.".format(USER_SANDSTORM_DIR)) 386 | os.makedirs(USER_SANDSTORM_DIR) 387 | USER_SANDSTORM_CACHE_DIR = os.path.join(USER_SANDSTORM_DIR, "caches") 388 | if not os.path.exists(USER_SANDSTORM_CACHE_DIR): 389 | print("Creating {} to hold sandstorm installer caches.".format(USER_SANDSTORM_CACHE_DIR)) 390 | os.makedirs(USER_SANDSTORM_CACHE_DIR) 391 | # Sandstorm is unhappy if you give it a keyring path that doesn't exist, 392 | # but is totally happy if that file is empty. So ensure a file exists. 393 | keyring_file = os.path.join(USER_SANDSTORM_DIR, "sandstorm-keyring") 394 | if not os.path.exists(keyring_file): 395 | with open(keyring_file, "wb") as f: 396 | pass 397 | 398 | class StackPlugin(object): 399 | def __init__(self, plugin_name): 400 | self._plugin_name = plugin_name 401 | plugin_dir = os.path.join(CODE_DIR, "stacks", self._plugin_name) 402 | if not os.path.exists(plugin_dir): 403 | raise Exception("No stack plugin for {}".format(plugin_name)) 404 | if (not os.path.exists(os.path.join(plugin_dir, "setup.sh")) or 405 | not os.path.exists(os.path.join(plugin_dir, "launcher.sh"))): 406 | raise Exception("Stack plugins require both 'setup.sh' and 'launcher.sh' scripts.") 407 | 408 | def plugin_file(self, filename): 409 | return os.path.join(CODE_DIR, "stacks", self._plugin_name, filename) 410 | 411 | def init_args(self): 412 | args_file = os.path.join(CODE_DIR, "stacks", self._plugin_name, "initargs") 413 | if os.path.exists(args_file): 414 | with open(args_file) as f: 415 | return f.read().strip() 416 | else: 417 | return "" 418 | 419 | def switch_to_https_cdn_resources(input_file_contents): 420 | # Let's change HTML with regular expressions. I realize that is a 421 | # dangerous thing to do, but I like to live dangerously (and 422 | # moreover we're changing Meteor templates so it's not like I can 423 | # "just" parse with a HTML parser and then serialize). 424 | changed = False 425 | input_output_pairs = ( 426 | ('http://fonts.googleapis.com', 'https://fonts.googleapis.com'), 427 | ) 428 | meta_regexes = [ 429 | '''(href=")(%s)''', 430 | '''(href=')(%s)''', 431 | ] 432 | output = input_file_contents 433 | 434 | for (bad, good) in input_output_pairs: 435 | for regex in meta_regexes: 436 | replacer_function = lambda matches: (matches.group(1) + good) 437 | output = re.sub(regex % (bad,), replacer_function, output) 438 | 439 | return output 440 | 441 | 442 | def auto(args): 443 | # Automatic packaging. This assumes: 444 | # 445 | # - There is no .sandstorm/ yet, and 446 | # 447 | # - The app uses git, and 448 | # 449 | # - It is OK to create a package with a key (package ID) that will 450 | # get lost, and 451 | # 452 | # - The user knows how to install the result on a Sandstorm 453 | # install. 454 | 455 | # Ensure this directory has a .meteor/. If not, bail out early and ask 456 | # the user to "cd" into a directory that does. 457 | stack = None 458 | STACK_TYPE_METEOR = 'meteor' 459 | if args.command_specific_args[0] == 'meteor': 460 | stack = STACK_TYPE_METEOR 461 | 462 | if stack == STACK_TYPE_METEOR: 463 | if not os.path.exists('.meteor'): 464 | print("ERROR", "Automatic packaging cannot proceed since you are not in a meteor project.") 465 | print("You will have to `cd` into another directory before running this too.") 466 | glob_matches = glob.glob('*/.meteor') 467 | if glob_matches: 468 | print('') 469 | print('Try running: cd', glob_matches[0][:-len('.meteor')]) 470 | sys.exit(1) 471 | 472 | # If this is a Meteor app, modify the app: 473 | # 474 | # - Add the Sandstorm accounts package, for auto-sign-in. 475 | # 476 | # - Maybe in the future, fix HTTP/HTTPS mixed content stuff on 477 | # common CDN domains. Note that the real fix for this is to 478 | # embed a copy of the assets in the app package, but this works 479 | # for now. 480 | if stack == STACK_TYPE_METEOR: 481 | ACCOUNTS_PKG = 'kenton:accounts-sandstorm' 482 | if ACCOUNTS_PKG not in open('.meteor/packages').read(): 483 | with open('.meteor/packages', 'a') as fd: 484 | fd.write('\n') 485 | fd.write(ACCOUNTS_PKG) 486 | fd.write('\n') 487 | 488 | # We can use `git`, so let's use it to loop over the files 489 | # in the repo and search for well-known HTTP resource links, 490 | # replacing them with HTTPS resource links. 491 | git_ls_files_executor = subprocess.Popen(['git', 'ls-files', '--', '*.html'], 492 | stdout=subprocess.PIPE) 493 | exit_code = git_ls_files_executor.wait() 494 | assert exit_code == 0 495 | stdout = git_ls_files_executor.communicate()[0] 496 | for filename in stdout.split('\n'): 497 | filename = filename.strip() 498 | if not filename: 499 | continue 500 | with open(filename, 'r') as fd: 501 | original_text = fd.read() 502 | https_ified = switch_to_https_cdn_resources(original_text) 503 | if https_ified != original_text: 504 | print('** Converting', filename, 'to HTTPS resources.') 505 | with open(filename, 'w') as fd: 506 | fd.write(https_ified) 507 | 508 | # setupvm 509 | print('$', 'vagrant-spk setup', ' '.join(args.command_specific_args)) 510 | setup_vm(args) 511 | 512 | # Remove any command-specific args for now. 513 | args.command_specific_args = [] 514 | 515 | # up 516 | print('$', 'vagrant-spk up') 517 | bring_up_vm(args) 518 | 519 | # init. This stores a fresh private key. 520 | print('$', 'vagrant-spk init') 521 | init(args) 522 | 523 | # Modify the .sandstorm/sandstorm-pkgdef.capnp to contain 524 | # the git repo name in some way, if there is a git repo here. 525 | git_repo = None 526 | git_remote_executor = subprocess.Popen(['git', 'remote', '-v'], stdout=subprocess.PIPE) 527 | exit_code = git_remote_executor.wait() 528 | if exit_code == 0: 529 | stdout = git_remote_executor.communicate()[0] 530 | git_repo = stdout.split('\n')[0].split()[1] 531 | 532 | if git_repo is not None: 533 | with open('.sandstorm/sandstorm-pkgdef.capnp') as fd: 534 | orig_capnp = fd.read() 535 | 536 | # Fix codeUrl = 537 | assert 'codeUrl = "http://example.com",' in orig_capnp 538 | improved_capnp = orig_capnp.replace( 539 | 'codeUrl = "http://example.com",', 540 | 'codeUrl = "{0}",'.format(git_repo)) 541 | 542 | # Fix appTitle = 543 | nicertitle = git_repo.split('/')[-1] 544 | assert '(defaultText = "Example App")' in orig_capnp 545 | improved_capnp = improved_capnp.replace( 546 | '(defaultText = "Example App")', 547 | '(defaultText = "{0}")'.format(nicertitle)) 548 | 549 | with open('.sandstorm/sandstorm-pkgdef.capnp', 'w') as fd: 550 | fd.write(improved_capnp) 551 | 552 | # Run build, but skip spk dev. TODO make less hacky. 553 | # Skip dev mode, since this is non-interactive for now. 554 | print('$', 'vagrant-spk dev') 555 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 556 | call_vagrant_command(sandstorm_dir, "ssh", "-c", " && ".join([ 557 | "/opt/app/.sandstorm/build.sh", 558 | ])) 559 | 560 | print("*** Click around and make sure the app works OK ***") 561 | 562 | # TODO: Replace 'Example app' with 'Fantastic app'. 563 | 564 | # Package it. 565 | args.command_specific_args = ['../app.spk'] 566 | print('$', 'vagrant-spk pack', ' '.join(args.command_specific_args)) 567 | pack(args) 568 | args.command_specific_args = [] 569 | 570 | # Success. Bring down the VM for now, since otherwise port 6090 571 | # will be in use. 572 | print('') 573 | print('You can run:') 574 | print('$ vagrant-spk vm halt') 575 | print('') 576 | print('When you are ready to stop thinking about packaging this app.') 577 | 578 | 579 | def setup_vm(args): 580 | if (len(args.command_specific_args) == 0): 581 | # No stack specified. List known stacks. 582 | print("No stack specified. Try specifying a stack with:") 583 | print("") 584 | print("vagrant-spk setupvm ") 585 | print("") 586 | print("Supported stacks:") 587 | stacks_dir = os.path.join(CODE_DIR, "stacks") 588 | for stack in sorted(os.listdir(stacks_dir)): 589 | print(" {}".format(stack)) 590 | print("") 591 | sys.exit(1) 592 | 593 | stack_plugin_name = args.command_specific_args[0] 594 | stack_plugin = StackPlugin(stack_plugin_name) 595 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 596 | print("Initializing .sandstorm directory in {}".format(sandstorm_dir)) 597 | # Create .sandstorm directory 598 | if not os.path.isdir(sandstorm_dir): 599 | os.makedirs(sandstorm_dir, mode=0o755) 600 | 601 | # Make sure ~/.sandstorm exists for storing signing keys 602 | ensure_host_sandstorm_folder_exists() 603 | 604 | # Copy global setup script to e.g. install and configure sandstorm 605 | global_setup_script_path = os.path.join(sandstorm_dir, "global-setup.sh") 606 | with open(global_setup_script_path, "wb") as f: 607 | f.write(GLOBAL_SETUP_SCRIPT.encode("UTF-8")) 608 | os.chmod(global_setup_script_path, 0o755) 609 | 610 | # Copy stack-specific script to e.g. install and configure nginx, mysql, and php5-fpm 611 | setup_script_path = os.path.join(sandstorm_dir, "setup.sh") 612 | with open(setup_script_path, "wb") as f: 613 | with open(stack_plugin.plugin_file("setup.sh"), "rb") as g: 614 | f.write(g.read()) 615 | os.chmod(setup_script_path, 0o755) 616 | 617 | # Copy build script, if present, to sandstorm root. If none is provided by 618 | # this stack, add an empty one so that users can customize it if needed. 619 | build_script_path = os.path.join(sandstorm_dir, "build.sh") 620 | with open(build_script_path, "wb") as f: 621 | source_script_path = stack_plugin.plugin_file("build.sh") 622 | if os.path.exists(source_script_path): 623 | with open(source_script_path, "rb") as g: 624 | f.write(g.read()) 625 | else: 626 | f.write(EMPTY_BUILD_SCRIPT) 627 | os.chmod(build_script_path, 0o755) 628 | 629 | # Copy default launcher script to sandstorm root for spk tracking 630 | launcher_script_path = os.path.join(sandstorm_dir, "launcher.sh") 631 | with open(launcher_script_path, "wb") as f: 632 | with open(stack_plugin.plugin_file("launcher.sh")) as g: 633 | f.write(g.read().encode("UTF-8")) 634 | os.chmod(launcher_script_path, 0o755) 635 | 636 | # Copy in Vagrantfile 637 | vagrantfile_path = os.path.join(sandstorm_dir, "Vagrantfile") 638 | with open(vagrantfile_path, "w") as f: 639 | f.write(VAGRANTFILE_CONTENTS) 640 | 641 | # Recursively copy in anything in the stack's service-config folder. 642 | source_service_config_dir = stack_plugin.plugin_file("service-config") 643 | target_service_config_dir = os.path.join(sandstorm_dir, "service-config") 644 | 645 | # Copy in a .gitignore file. See https://github.com/sandstorm-io/vagrant-spk/issues/30 646 | gitignore_path = os.path.join(sandstorm_dir, ".gitignore") 647 | with open(gitignore_path, "ab") as f: 648 | f.write(GITIGNORE_CONTENTS.encode("UTF-8")) 649 | 650 | # Copy in a .gitattributes file. See https://github.com/sandstorm-io/vagrant-spk/issues/55 651 | gitattributes_path = os.path.join(sandstorm_dir, ".gitattributes") 652 | with open(gitattributes_path, "ab") as f: 653 | f.write(GITATTRIBUTES_CONTENTS.encode("UTF-8")) 654 | 655 | if os.path.exists(source_service_config_dir): 656 | if os.path.exists(target_service_config_dir): 657 | shutil.rmtree(target_service_config_dir) 658 | shutil.copytree(source_service_config_dir, target_service_config_dir) 659 | 660 | # Make a note of which stack was used 661 | stack_path = os.path.join(sandstorm_dir, "stack") 662 | with open(stack_path, "w") as f: 663 | f.write(stack_plugin_name + "\n") 664 | 665 | def upgrade_vm(args): 666 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 667 | print("Upgrading VM parameters in {}".format(sandstorm_dir)) 668 | # Copy global setup script to e.g. install and configure sandstorm 669 | global_setup_script_path = os.path.join(sandstorm_dir, "global-setup.sh") 670 | with open(global_setup_script_path, "wb") as f: 671 | f.write(GLOBAL_SETUP_SCRIPT.encode("UTF-8")) 672 | os.chmod(global_setup_script_path, 0o755) 673 | # Copy in Vagrantfile 674 | vagrantfile_path = os.path.join(sandstorm_dir, "Vagrantfile") 675 | with open(vagrantfile_path, "w") as f: 676 | f.write(VAGRANTFILE_CONTENTS) 677 | 678 | def bring_up_vm(args): 679 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 680 | # Make sure ~/.sandstorm exists, since global-setup.sh uses 681 | # ~/.sandstorm/caches to cache the installers 682 | ensure_host_sandstorm_folder_exists() 683 | # Sanity-check that the user will get a Vagrant box that supports vboxsf, since 684 | # vagrant-spk depends on that for now. 685 | ensure_working_vboxsf_in_base_box(sandstorm_dir) 686 | # Bring up VM 687 | call_vagrant_command(sandstorm_dir, "up") 688 | 689 | def init(args): 690 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 691 | # Figure out which stack created this runtime, so we can load appropriate additional init args. 692 | stack_path = os.path.join(sandstorm_dir, "stack") 693 | with open(stack_path) as f: 694 | stack = f.read().strip() 695 | stack_plugin = StackPlugin(stack) 696 | init_args = stack_plugin.init_args() 697 | # Initialize the package with spk init 698 | call_vagrant_command(sandstorm_dir, "ssh", "-c", "spk init -p 8000 --keyring=/host-dot-sandstorm/sandstorm-keyring --output=/opt/app/.sandstorm/sandstorm-pkgdef.capnp {} -- /bin/bash /opt/app/.sandstorm/launcher.sh".format(init_args)) 699 | 700 | def dev(args): 701 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 702 | call_vagrant_command(sandstorm_dir, "ssh", "-c", " && ".join([ 703 | "/opt/app/.sandstorm/build.sh", 704 | "cd /opt/app/.sandstorm", 705 | "spk dev --pkg-def=/opt/app/.sandstorm/sandstorm-pkgdef.capnp:pkgdef" 706 | ])) 707 | 708 | def inject_enter_grain_into_grain(sandstorm_dir, pid, enter_grain_checksum, enter_grain_binary, 709 | tmp_vagrant_spk_dir, enter_grain_binary_path): 710 | ### The purpose of this function is to take a PID, which is presumably the root process of a 711 | ### grain within the vagrant-spk VM, and make sure that /tmp/vagrant-spk/enter_grain is the 712 | ### binary that we want. If is isn't, then inject it. 713 | tmp_vagrant_spk_dir = '/proc/{}/root{}'.format(pid, TMP_VAGRANT_SPK_LOCATION) 714 | sys.stderr.write("Adding enter_grain program to the grain in {}/enter-grain...\n".format(TMP_VAGRANT_SPK_LOCATION)) 715 | enter_grain_binary_path = tmp_vagrant_spk_dir + '/enter-grain' 716 | commands = ' && '.join([ 717 | # Make sure /tmp/vagrant-spk exists in the VM. 718 | 'sudo mkdir -p {}'.format(tmp_vagrant_spk_dir), 719 | # If the sha1sum tool exists, and the binary in this directory has the right sha1sum, 720 | # then skip sending it over. 721 | 'if which sha1sum >/dev/null && sudo sha1sum {} 2>/dev/null | grep -q ^{} ; then sudo chmod 755 {} && exit 0 ; fi'.format( 722 | enter_grain_binary_path, enter_grain_checksum, enter_grain_binary_path), 723 | # If not, then use dd to inject it in. dd is more convenient than cat here because dd opens 724 | # the file itself, so `sudo dd` can easily write to files that the "vagrant" user can't. 725 | 'sudo dd of={} 2>/dev/null'.format(enter_grain_binary_path), 726 | 'sudo chmod 755 {}'.format(enter_grain_binary_path), 727 | ]) 728 | call_vagrant_ssh_command_providing_stdin( 729 | sandstorm_dir, 730 | commands, 731 | stdin=enter_grain_binary) 732 | 733 | def shell(args): 734 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 735 | try: 736 | filename = os.path.join(CODE_DIR, 'helpers', 'enter_grain') 737 | with open(filename, 'rb') as fd: 738 | enter_grain_binary = fd.read() 739 | # There should be a .sha1 file next to the binary, just to make sure that it is not 740 | # accidentally corrupted. If it is the empty string, ignore it; this way, we can avoid 741 | # checksum errors while developing the binary. 742 | with open(os.path.join(CODE_DIR, 'helpers', 'enter_grain.sha1'), 'rb') as fd: 743 | # The first thing in the file, if you split by spaces, is a hex value. There may be 744 | # other text after it, namely the filename that was checksummed. 745 | desired_checksum = fd.read().decode('utf-8').split(' ')[0].strip() 746 | found_checksum = hashlib.sha1(enter_grain_binary).hexdigest() 747 | if (desired_checksum and (desired_checksum != found_checksum)): 748 | raise RuntimeError("ERROR: This vagrant-spk bundle seems to be corrupt: bad checksum on enter_grain binary.") 749 | except Exception as e: 750 | sys.stderr.write("ERROR: Failed to load enter_grain binary.") 751 | raise 752 | sys.stderr.write("Looking for apps in dev mode...\n") 753 | # The following vagrant-spk ssh command checks for two things. 754 | # 755 | # - Is spk dev running? and 756 | # - What are the supervisor processes? 757 | # 758 | # The supervisor process check also sanity-checks that they are owned by 759 | # a user ID called sandstorm, which vagrant-spk assumes is the value of 760 | # SERVER_USER in sandstorm.conf, and looks for the grain ID by looking at 761 | # argv of the supervisor - the grain ID is the first parameter that does 762 | # not contain a hyphen. It also searches for the first child process of the 763 | # supervisor, sorted nondeterministically, but that should be OK because the 764 | # supervisor should have only one child process. 765 | # 766 | # vagrant is slow to start up, so we run both checks in the same vagrant 767 | # ssh invocation. 768 | # 769 | # For safety on Windows especially, and to make it easier to manually test this on Mac/Linux via 770 | # "vagrant ssh -c '...'", we avoid using any of these characters within the string: 771 | # 772 | # - apostrophe: ' 773 | # - quotation mark: " 774 | # - backslash: \ 775 | data_from_guest = call_vagrant_ssh_command_capturing_output( 776 | sandstorm_dir, 777 | r'''pidof spk || echo no-spk;''' + 778 | r'''SANDSTORM_UID=$(id sandstorm | sed -r s,[^0-9],_,g | sed -r s,_+,_,g | cut -d _ -f 2 ) ; ''' + 779 | r'''for pid in $(pidof supervisor); do echo $pid $(cat /proc/$pid/status | grep -q 'Uid:.*'${SANDSTORM_UID} && echo ownership-correct || echo ownership-wrong) $(xargs -0 -n1 echo < /proc/$pid/cmdline | grep -v -- - | head -n2 | tail -n1) $(grep -E -l ^PPid:[[:blank:]]*${pid}$ /proc/*/status | head -n1 | sed -r s,/proc/,,g | sed -r s,/status,,) ; done''' 780 | # get pid of supervisor, check process owner, get first child process 781 | ) 782 | splitted = data_from_guest.decode('utf-8').splitlines() 783 | spk_pid = splitted[0] 784 | if spk_pid == 'no-spk': 785 | sys.stderr.write("No Sandstorm supervisor processes found.\n" + 786 | "Try `vagrant-spk dev` first.\n") 787 | sys.exit(1) 788 | supervisor_pid_info = splitted[1:] 789 | sys.stderr.write("Looking for grains...\n") 790 | if supervisor_pid_info: 791 | sys.stderr.write('\n') 792 | else: 793 | sys.stderr.write("No Sandstorm supervisor processes found.\n" + 794 | "Make sure to open a grain of this app in your web browser.\n") 795 | sys.exit(1) 796 | supervisors = [] 797 | for line in supervisor_pid_info: 798 | try: 799 | supervisor_pid, ownership_check, grain_id, child_pid = line.split() 800 | except: 801 | sys.stderr.write('Error parsing line: %s. Crashing.' % line) 802 | raise 803 | if ownership_check == 'ownership-correct': 804 | supervisors.append({ 805 | 'supervisor_pid': supervisor_pid, 806 | 'grain_id': grain_id, 807 | 'child_pid': int(child_pid), 808 | }) 809 | grain_choice_human_readable = input(format_shell_grain_choices(supervisors)).strip() 810 | if not grain_choice_human_readable: 811 | grain_choice_human_readable = "1" # by default 812 | try: 813 | grain_choice_as_int = int(grain_choice_human_readable) # parse into an int 814 | chosen_supervisor = supervisors[grain_choice_as_int - 1] 815 | except (ValueError, IndexError): 816 | sys.stderr.write("Please enter a valid number (e.g. 1).\n") 817 | sys.exit(1) 818 | 819 | sys.stderr.write("OK. Chosen grain ID %s, whose child PID is %d. Attaching...\n" % 820 | (chosen_supervisor['grain_id'], chosen_supervisor['child_pid'])) 821 | pid = chosen_supervisor['child_pid'] 822 | tmp_vagrant_spk_dir = '/proc/{}/root{}'.format(pid, TMP_VAGRANT_SPK_LOCATION) 823 | enter_grain_binary_path = tmp_vagrant_spk_dir + '/enter-grain' 824 | inject_enter_grain_into_grain( 825 | sandstorm_dir, 826 | chosen_supervisor['child_pid'], 827 | desired_checksum, 828 | enter_grain_binary, 829 | tmp_vagrant_spk_dir, 830 | enter_grain_binary_path, 831 | ) 832 | call_vagrant_command(sandstorm_dir, "ssh", "-c", " && ".join([ 833 | "cd /opt/app/.sandstorm", 834 | "sudo {} {}".format(enter_grain_binary_path, pid), 835 | ])) 836 | sys.stderr.write( 837 | "NOTE: You should discard all sandstorm-files.list changes from this session to avoid bloat!\n") 838 | 839 | def pack(args): 840 | output_spk = args.command_specific_args[0] 841 | print(output_spk) 842 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 843 | # spk doesn't like to build packages on the vboxsf filesystem due to some 844 | # oddity when you unlink an open file, so we build the package in the guest 845 | # homedir, then move it out to the host. 846 | call_vagrant_command(sandstorm_dir, "ssh", "-c", " && ".join([ 847 | "cd /opt/app/.sandstorm/", 848 | "spk pack --keyring=/host-dot-sandstorm/sandstorm-keyring --pkg-def=/opt/app/.sandstorm/sandstorm-pkgdef.capnp:pkgdef /home/vagrant/sandstorm-package.spk", 849 | "spk verify --details /home/vagrant/sandstorm-package.spk", 850 | "mv /home/vagrant/sandstorm-package.spk /opt/app/sandstorm-package.spk" 851 | ])) 852 | shutil.move("sandstorm-package.spk", output_spk) 853 | print("package produced at {}".format(output_spk)) 854 | 855 | def verify(args): 856 | spk = args.command_specific_args[0] 857 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 858 | spk_basename = os.path.basename(spk) 859 | temp_spk = os.path.join(sandstorm_dir, spk_basename) 860 | 861 | try: 862 | shutil.copyfile(spk, temp_spk) 863 | call_vagrant_command(sandstorm_dir, "ssh", "-c", "spk verify --details /opt/app/.sandstorm/{}".format(spk_basename)) 864 | finally: 865 | os.remove(temp_spk) 866 | 867 | def publish(args): 868 | if len(args.command_specific_args) == 0: 869 | print("Please specify an .spk file to publish - e.g.") 870 | print(" vagrant-spk publish example.spk") 871 | sys.exit(1) 872 | # Sadly, we have to copy the spk into the VM since it could be anywhere on the host filesystem 873 | spk = args.command_specific_args[0] 874 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 875 | spk_basename = os.path.basename(spk) 876 | temp_spk = os.path.join(sandstorm_dir, spk_basename) 877 | 878 | try: 879 | shutil.copyfile(spk, temp_spk) 880 | call_vagrant_command(sandstorm_dir, "ssh", "-c", "spk publish --keyring=/host-dot-sandstorm/sandstorm-keyring /opt/app/.sandstorm/{}".format(spk_basename)) 881 | finally: 882 | os.remove(temp_spk) 883 | 884 | def keygen(args): 885 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 886 | call_vagrant_command(sandstorm_dir, "ssh", "-c", 887 | "spk keygen --keyring=/host-dot-sandstorm/sandstorm-keyring {}".format( 888 | " ".join(args.command_specific_args) 889 | ) 890 | ) 891 | 892 | def listkeys(args): 893 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 894 | call_vagrant_command(sandstorm_dir, "ssh", "-c", 895 | "spk listkeys --keyring=/host-dot-sandstorm/sandstorm-keyring {}".format( 896 | " ".join(args.command_specific_args) 897 | ) 898 | ) 899 | 900 | def getkey(args): 901 | key_id = args.command_specific_args[0] 902 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 903 | call_vagrant_command(sandstorm_dir, "ssh", "-c", 904 | "spk getkey --keyring=/host-dot-sandstorm/sandstorm-keyring {}".format(key_id), 905 | "--", "-T" # spk getkey refuses to print the output unless isatty() returns false, 906 | # so we make sure to disable TTY allocation. 907 | ) 908 | 909 | def vm_subcommand(args): 910 | sandstorm_dir = os.path.join(args.work_directory, ".sandstorm") 911 | if len(args.command_specific_args) == 0: 912 | sys.stderr.write("Try specifying a command to pass through to vagrant, like\n\nvagrant-spk vm up\n\n") 913 | sys.exit(1) 914 | 915 | # Do a couple sanity checks. 916 | ensure_host_sandstorm_folder_exists() 917 | if not os.path.exists(sandstorm_dir) or not os.path.exists(os.path.join(sandstorm_dir, "Vagrantfile")): 918 | sys.stderr.write("You can't call `vm` commands if there's no `.sandstorm` folder.\n" + 919 | "Try `vagrant-spk setupvm ` first.\n") 920 | sys.exit(1) 921 | 922 | subcommand = args.command_specific_args[0] 923 | # Do subcommand specific sanity checks. 924 | if subcommand == 'up': 925 | # Sanity-check that the user will get a Vagrant box that supports vboxsf, since 926 | # vagrant-spk depends on that for now. 927 | ensure_working_vboxsf_in_base_box(sandstorm_dir) 928 | 929 | call_vagrant_command(sandstorm_dir, *args.command_specific_args) 930 | 931 | 932 | class Command(object): 933 | def __init__(self, name, func, helptext, hidden=False): 934 | self.name = name 935 | self.func = func # func may be None if this is intended only for the help text 936 | self.helptext = helptext 937 | self.hidden = hidden # if true, do not show in vagrant-spk --help 938 | 939 | 940 | def main(): 941 | global ENABLE_EXPERIMENTAL 942 | # Switch on feature(s) that we don't think are ready for everyone. 943 | if os.environ.get("VAGRANT_SPK_EXPERIMENTAL", "").upper() == "Y": 944 | ENABLE_EXPERIMENTAL = True 945 | 946 | operations = [ 947 | Command('setupvm', setup_vm, 948 | "Set up a fresh app environment in \n" 949 | " --work-directory (using the provided `stack` \n" 950 | " command arg). Also inits developer keys in \n" 951 | " ~/.sandstorm on this host."), 952 | Command('upgradevm', upgrade_vm, 953 | "Upgrade the Vagrant VM and setup scripts to \n" 954 | " latest version included in vagrant-spk."), 955 | Command('vm', vm_subcommand, 956 | "Pass through all subsequent arguments to `vagrant` run in `.sandstorm`."), 957 | Command('vm up', None, "Start the Vagrant VM (and build the VM image if needed)"), 958 | Command('vm halt', None, "Shut down the Vagrant VM."), 959 | Command('vm destroy', None, "Delete the Vagrant VM (but leave --work-directory untouched)."), 960 | Command('vm ssh', None, "SSH into the Vagrant VM."), 961 | Command('init', init, 962 | "Initialize fresh Sandstorm package definition \n" 963 | " resources in --work-directory."), 964 | Command('dev', dev, 965 | "Make the current app available to Sandstorm \n" 966 | " in dev mode (after bringing the Vagrant VM `up`)"), 967 | Command('enter-grain', shell, "Get a shell within a grain"), 968 | Command('pack', pack, 969 | "Pack the curent app into a \n" 970 | " Sandstorm package."), 971 | Command('verify', verify, 972 | "Show details of the specified Sandstorm package."), 973 | Command('publish', publish, 974 | "Send the current packed app spk to \n" 975 | " the Sandstorm App Market."), 976 | Command('keygen', keygen, 977 | "Generate a new key, add it to your keyring,\n" 978 | " and print the identifier."), 979 | Command('listkeys', listkeys, 980 | "List the keys in your keyring."), 981 | Command('getkey', getkey, 982 | "Given a key identifier, retrieve and print the\n" 983 | " corresponding private key from your keyring."), 984 | ] 985 | 986 | if ENABLE_EXPERIMENTAL: 987 | operations.extend([ 988 | Command('auto', auto, 989 | "Automatically package the app in \n" 990 | " --work-directory (using the provided `stack` \n" 991 | " command arg. Also inits developer keys in \n" 992 | " ~/.sandstorm on this host. (EXPERIMENTAL)"), 993 | ]) 994 | 995 | 996 | op_to_func = dict((c.name, c.func) for c in operations if c.func) 997 | ops_helptext = '\n'.join(c.name + ': ' + c.helptext for c in operations if not c.hidden) 998 | parser = argparse.ArgumentParser(prog=sys.argv[0], formatter_class=argparse.RawTextHelpFormatter) 999 | parser.add_argument("command", choices=[c.name for c in operations], help=ops_helptext) 1000 | parser.add_argument("command_specific_args", nargs="*") 1001 | parser.add_argument("-V", "--version", action="version", version="vagrant-spk "+__version__) 1002 | parser.add_argument( 1003 | "--work-directory", 1004 | action='store', 1005 | default=PWD, 1006 | help="Use this working directory (e.g. for .sandstorm/ \n" 1007 | "and other configuration). [Default: current \n" 1008 | "working directory]") 1009 | args = parser.parse_args(sys.argv[1:]) 1010 | operation = op_to_func[args.command] 1011 | operation(args) 1012 | 1013 | if __name__ == "__main__": 1014 | main() 1015 | -------------------------------------------------------------------------------- /windows-support/Makefile: -------------------------------------------------------------------------------- 1 | all: dist/innosetup/vagrant-spk-setup.exe 2 | 3 | WINE := wine 4 | WINEARGS := WINEDEBUG=-all WINEPREFIX=$$PWD/state/wineprefix 5 | 6 | clean: 7 | rm -f dist/innosetup/vagrant-spk-setup.exe 8 | 9 | distclean: 10 | rm -rf state build vendor dist ../build ../dist vagrant-spk.spec 11 | 12 | wine: /usr/bin/wine 13 | sudo apt-get install wine wine32 14 | 15 | vendor: 16 | mkdir -p vendor 17 | 18 | state: 19 | mkdir -p state 20 | 21 | vendor/python.exe: | vendor state/wineprefix 22 | wget https://www.python.org/ftp/python/3.11.6/python-3.11.6.exe -O vendor/python.exe 23 | touch vendor/python.exe 24 | 25 | vendor/python.exe.installed: | vendor/python.exe 26 | cp vendor/python.exe vendor/python-tmp.exe 27 | $(WINEARGS) $(WINE) vendor/python-tmp.exe /quiet InstallAllUsers=1 PrependPath=1 Include_test=0 AssociateFiles=0 Shortcuts=0 28 | touch vendor/python.exe.installed 29 | 30 | state/pyinstaller.installed: | vendor/python.exe.installed 31 | $(WINEARGS) $(WINE) 'c:/Program Files (x86)/Python311-32/python.exe' -m pip install pyinstaller 32 | touch state/pyinstaller.installed 33 | 34 | dist/vagrant-spk.exe: ../vagrant-spk | state/pyinstaller.installed 35 | $(WINEARGS) $(WINE) 'c:/Program Files (x86)/Python311-32/Scripts/pyinstaller.exe' -F ../vagrant-spk 36 | 37 | vendor/innosetup.exe: | vendor 38 | wget 'http://www.jrsoftware.org/download.php/is.exe?site=1' -O vendor/innosetup.exe 39 | 40 | vendor/innosetup.exe.installed: | state/wineprefix vendor/innosetup.exe 41 | $(WINEARGS) $(WINE) vendor/innosetup.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /ALLUSERS /NOICONS 42 | touch vendor/innosetup.exe.installed 43 | 44 | state/version.iss: ../vagrant-spk 45 | ../vagrant-spk --version | sed -e 's,vagrant-spk ,,' | xargs printf '#define MyAppVersion "%s"\n' > state/version.iss 46 | 47 | dist/innosetup/vagrant-spk-setup.exe: dist/vagrant-spk.exe | vendor/innosetup.exe.installed state/version.iss 48 | # Some WINE installs are seemingly 64-bit but install InnoSetup to Program Files, not 49 | # Program Files (x86). We work around this with a symlink. 50 | if [ ! -d 'state/wineprefix/drive_c/Program Files (x86)' ] ; then ln -s 'Program Files' 'state/wineprefix/drive_c/Program Files (x86)' ; fi 51 | $(WINEARGS) $(WINE) 'c:/program files (x86)/inno setup 6/iscc.exe' windows-installer.iss 52 | 53 | state/regdata: | state 54 | printf 'Windows Registry Editor Version 5.00\n\n[HKEY_CURRENT_USER\\Software\\Wine\\WineDbg]\n"ShowCrashDialog"=dword:00000000\n\n[HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion]\n"CurrentVersion"="10.0"\n\n' > state/regdata 55 | 56 | state/wineprefix: state/regdata 57 | mkdir -p state/wineprefix 58 | $(WINEARGS) $(WINE) wineboot 59 | $(WINEARGS) $(WINE) regedit /S state/regdata 60 | touch state/wineprefix 61 | -------------------------------------------------------------------------------- /windows-support/modpath.iss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // 3 | // Inno Setup Ver: 5.4.2 4 | // Script Version: 1.4.2 5 | // Author: Jared Breland 6 | // Homepage: http://www.legroom.net/software 7 | // License: GNU Lesser General Public License (LGPL), version 3 8 | // http://www.gnu.org/licenses/lgpl.html 9 | // 10 | // Script Function: 11 | // Allow modification of environmental path directly from Inno Setup installers 12 | // 13 | // Instructions: 14 | // Copy modpath.iss to the same directory as your setup script 15 | // 16 | // Add this statement to your [Setup] section 17 | // ChangesEnvironment=true 18 | // 19 | // Add this statement to your [Tasks] section 20 | // You can change the Description or Flags 21 | // You can change the Name, but it must match the ModPathName setting below 22 | // Name: modifypath; Description: &Add application directory to your environmental path; Flags: unchecked 23 | // 24 | // Add the following to the end of your [Code] section 25 | // ModPathName defines the name of the task defined above 26 | // ModPathType defines whether the 'user' or 'system' path will be modified; 27 | // this will default to user if anything other than system is set 28 | // setArrayLength must specify the total number of dirs to be added 29 | // Result[0] contains first directory, Result[1] contains second, etc. 30 | // const 31 | // ModPathName = 'modifypath'; 32 | // ModPathType = 'user'; 33 | // 34 | // function ModPathDir(): TArrayOfString; 35 | // begin 36 | // setArrayLength(Result, 1); 37 | // Result[0] := ExpandConstant('{app}'); 38 | // end; 39 | // #include "modpath.iss" 40 | // ---------------------------------------------------------------------------- 41 | 42 | procedure ModPath(); 43 | var 44 | oldpath: String; 45 | newpath: String; 46 | updatepath: Boolean; 47 | pathArr: TArrayOfString; 48 | aExecFile: String; 49 | aExecArr: TArrayOfString; 50 | i, d: Integer; 51 | pathdir: TArrayOfString; 52 | regroot: Integer; 53 | regpath: String; 54 | 55 | begin 56 | // Get constants from main script and adjust behavior accordingly 57 | // ModPathType MUST be 'system' or 'user'; force 'user' if invalid 58 | if ModPathType = 'system' then begin 59 | regroot := HKEY_LOCAL_MACHINE; 60 | regpath := 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'; 61 | end else begin 62 | regroot := HKEY_CURRENT_USER; 63 | regpath := 'Environment'; 64 | end; 65 | 66 | // Get array of new directories and act on each individually 67 | pathdir := ModPathDir(); 68 | for d := 0 to GetArrayLength(pathdir)-1 do begin 69 | updatepath := true; 70 | 71 | // Modify WinNT path 72 | if UsingWinNT() = true then begin 73 | 74 | // Get current path, split into an array 75 | RegQueryStringValue(regroot, regpath, 'Path', oldpath); 76 | oldpath := oldpath + ';'; 77 | i := 0; 78 | 79 | while (Pos(';', oldpath) > 0) do begin 80 | SetArrayLength(pathArr, i+1); 81 | pathArr[i] := Copy(oldpath, 0, Pos(';', oldpath)-1); 82 | oldpath := Copy(oldpath, Pos(';', oldpath)+1, Length(oldpath)); 83 | i := i + 1; 84 | 85 | // Check if current directory matches app dir 86 | if pathdir[d] = pathArr[i-1] then begin 87 | // if uninstalling, remove dir from path 88 | if IsUninstaller() = true then begin 89 | continue; 90 | // if installing, flag that dir already exists in path 91 | end else begin 92 | updatepath := false; 93 | end; 94 | end; 95 | 96 | // Add current directory to new path 97 | if i = 1 then begin 98 | newpath := pathArr[i-1]; 99 | end else begin 100 | newpath := newpath + ';' + pathArr[i-1]; 101 | end; 102 | end; 103 | 104 | // Append app dir to path if not already included 105 | if (IsUninstaller() = false) AND (updatepath = true) then 106 | newpath := newpath + ';' + pathdir[d]; 107 | 108 | // Write new path 109 | RegWriteStringValue(regroot, regpath, 'Path', newpath); 110 | 111 | // Modify Win9x path 112 | end else begin 113 | 114 | // Convert to shortened dirname 115 | pathdir[d] := GetShortName(pathdir[d]); 116 | 117 | // If autoexec.bat exists, check if app dir already exists in path 118 | aExecFile := 'C:\AUTOEXEC.BAT'; 119 | if FileExists(aExecFile) then begin 120 | LoadStringsFromFile(aExecFile, aExecArr); 121 | for i := 0 to GetArrayLength(aExecArr)-1 do begin 122 | if IsUninstaller() = false then begin 123 | // If app dir already exists while installing, skip add 124 | if (Pos(pathdir[d], aExecArr[i]) > 0) then 125 | updatepath := false; 126 | break; 127 | end else begin 128 | // If app dir exists and = what we originally set, then delete at uninstall 129 | if aExecArr[i] = 'SET PATH=%PATH%;' + pathdir[d] then 130 | aExecArr[i] := ''; 131 | end; 132 | end; 133 | end; 134 | 135 | // If app dir not found, or autoexec.bat didn't exist, then (create and) append to current path 136 | if (IsUninstaller() = false) AND (updatepath = true) then begin 137 | SaveStringToFile(aExecFile, #13#10 + 'SET PATH=%PATH%;' + pathdir[d], True); 138 | 139 | // If uninstalling, write the full autoexec out 140 | end else begin 141 | SaveStringsToFile(aExecFile, aExecArr, False); 142 | end; 143 | end; 144 | end; 145 | end; 146 | 147 | // Split a string into an array using passed delimeter 148 | procedure MPExplode(var Dest: TArrayOfString; Text: String; Separator: String); 149 | var 150 | i: Integer; 151 | begin 152 | i := 0; 153 | repeat 154 | SetArrayLength(Dest, i+1); 155 | if Pos(Separator,Text) > 0 then begin 156 | Dest[i] := Copy(Text, 1, Pos(Separator, Text)-1); 157 | Text := Copy(Text, Pos(Separator,Text) + Length(Separator), Length(Text)); 158 | i := i + 1; 159 | end else begin 160 | Dest[i] := Text; 161 | Text := ''; 162 | end; 163 | until Length(Text)=0; 164 | end; 165 | 166 | 167 | procedure CurStepChanged(CurStep: TSetupStep); 168 | var 169 | taskname: String; 170 | begin 171 | taskname := ModPathName; 172 | if CurStep = ssPostInstall then 173 | if IsTaskSelected(taskname) then 174 | ModPath(); 175 | end; 176 | 177 | procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); 178 | var 179 | aSelectedTasks: TArrayOfString; 180 | i: Integer; 181 | taskname: String; 182 | regpath: String; 183 | regstring: String; 184 | appid: String; 185 | begin 186 | // only run during actual uninstall 187 | if CurUninstallStep = usUninstall then begin 188 | // get list of selected tasks saved in registry at install time 189 | appid := '{#emit SetupSetting("AppId")}'; 190 | if appid = '' then appid := '{#emit SetupSetting("AppName")}'; 191 | regpath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\'+appid+'_is1'); 192 | RegQueryStringValue(HKLM, regpath, 'Inno Setup: Selected Tasks', regstring); 193 | if regstring = '' then RegQueryStringValue(HKCU, regpath, 'Inno Setup: Selected Tasks', regstring); 194 | 195 | // check each task; if matches modpath taskname, trigger patch removal 196 | if regstring <> '' then begin 197 | taskname := ModPathName; 198 | MPExplode(aSelectedTasks, regstring, ','); 199 | if GetArrayLength(aSelectedTasks) > 0 then begin 200 | for i := 0 to GetArrayLength(aSelectedTasks)-1 do begin 201 | if comparetext(aSelectedTasks[i], taskname) = 0 then 202 | ModPath(); 203 | end; 204 | end; 205 | end; 206 | end; 207 | end; 208 | 209 | function NeedRestart(): Boolean; 210 | var 211 | taskname: String; 212 | begin 213 | taskname := ModPathName; 214 | if IsTaskSelected(taskname) and not UsingWinNT() then begin 215 | Result := True; 216 | end else begin 217 | Result := False; 218 | end; 219 | end; -------------------------------------------------------------------------------- /windows-support/windows-installer.iss: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 3 | 4 | #define MyAppName "vagrant-spk" 5 | #include "state/version.iss" 6 | #define MyAppPublisher "Sandstorm Development Group, Inc." 7 | #define MyAppURL "https://docs.sandstorm.io/" 8 | #define MyAppExeName "vagrant-spk.exe" 9 | 10 | [Setup] 11 | ; NOTE: The value of AppId uniquely identifies this application. 12 | ; Do not use the same AppId value in installers for other applications. 13 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 14 | AppId={{1EAFBDE2-157F-46F4-B5AB-6858FD3A0D8A} 15 | AppName={#MyAppName} 16 | AppVersion={#MyAppVersion} 17 | ;AppVerName={#MyAppName} {#MyAppVersion} 18 | AppPublisher={#MyAppPublisher} 19 | AppPublisherURL={#MyAppURL} 20 | AppSupportURL={#MyAppURL} 21 | AppUpdatesURL={#MyAppURL} 22 | DefaultDirName={pf}\{#MyAppName} 23 | DefaultGroupName={#MyAppName} 24 | DisableProgramGroupPage=yes 25 | OutputDir=dist\innosetup 26 | OutputBaseFilename=vagrant-spk-setup 27 | Compression=lzma 28 | SolidCompression=yes 29 | ChangesEnvironment=true 30 | 31 | [Languages] 32 | Name: "english"; MessagesFile: "compiler:Default.isl" 33 | 34 | [Tasks] 35 | Name: modifypath; Description: &Add application directory to your environmental path 36 | 37 | [Files] 38 | ; vagrant-spk itself 39 | Source: "dist\vagrant-spk.exe"; DestDir: "{app}"; Flags: ignoreversion 40 | 41 | ; vagrant-spk platform stacks 42 | Source: "..\stacks\*"; DestDir: "{app}\stacks"; Flags: recursesubdirs 43 | 44 | ; "helpers" - right now just the enter_grain binary and its sha1 45 | Source: "..\helpers\*"; DestDir: "{app}\helpers"; Flags: recursesubdirs 46 | 47 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 48 | 49 | [InstallDelete] 50 | Type: files; Name: "{app}\ssh.exe" 51 | 52 | [Code] 53 | const 54 | ModPathName = 'modifypath'; 55 | ModPathType = 'user'; 56 | 57 | function ModPathDir(): TArrayOfString; 58 | begin 59 | setArrayLength(Result, 2) 60 | Result[0] := ExpandConstant('{app}'); 61 | end; 62 | #include "modpath.iss" 63 | --------------------------------------------------------------------------------