diff --git a/.forgejo/workflows/book-pr.yaml b/.forgejo/workflows/book-pr.yaml deleted file mode 100644 index 7f978d1..0000000 --- a/.forgejo/workflows/book-pr.yaml +++ /dev/null @@ -1,61 +0,0 @@ -name: "Book - PR" - -on: - pull_request: - types: - - opened - - reopened - - synchronize - paths: - - ".forgejo/workflows/book-pr.yaml" - - "book/**" - -concurrency: - group: page-preview - cancel-in-progress: false - -jobs: - createPreview: - runs-on: nix - env: - SITE_ORIGIN: - "https://${{ forge.event.repository.owner.username - }}.preview.codeberg.page" - SITE_BASEPATH: - "/${{ forge.event.repository.name }}@${{ forge.event.number }}/" - steps: - - uses: actions/checkout@v4 - - uses: https://codeberg.org/icewind/attic-action@v1 - with: - name: link - instance: https://cache.icewind.link - authToken: "${{ secrets.ATTIC_TOKEN }}" - - name: Build - run: | - # git-pages/action doesn't like symlinks - cp -r $(nix build .#haze-book --print-out-paths --no-link) dist - - name: "Deploy Site" - uses: https://code.forgejo.org/actions/git-pages@v2 - with: - site: "${{ env.SITE_ORIGIN }}${{ env.SITE_BASEPATH }}" # - token: "${{ forge.token }}" # - source: "dist/" # - - name: "Find comment" - uses: "https://github.com/peter-evans/find-comment@v4" # - id: comment - with: - issue-number: ${{ forge.event.number }} # - body-includes: "" # - - name: "Create or update comment" - uses: "https://github.com/peter-evans/create-or-update-comment@v5" # - with: - issue-number: ${{ forge.event.number }} # - comment-id: "${{ steps.comment.outputs.comment-id }}" # - edit-mode: "replace" - body: |- - # Pull request preview ready! - - The Preview of commit ${{ forge.event.pull_request.head.sha }} has been created. - You can find it here: ${{ env.SITE_ORIGIN }}${{ env.SITE_BASEPATH }} - - diff --git a/.forgejo/workflows/book.yaml b/.forgejo/workflows/book.yaml deleted file mode 100644 index 05b4f11..0000000 --- a/.forgejo/workflows/book.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: "Book" - -on: - push: - branches: - - main - -jobs: - preview: - runs-on: nix - env: - SITE_ORIGIN: "${{forge.event.repository.owner.username}}.codeberg.page" - SITE_BASEPATH: "/${{forge.event.repository.name}}/" - steps: - - uses: actions/checkout@v4 - - uses: https://codeberg.org/icewind/attic-action@v1 - with: - name: link - instance: https://cache.icewind.link - authToken: "${{ secrets.ATTIC_TOKEN }}" - - name: Build - run: | - # git-pages/action doesn't like symlinks - cp -r $(nix build .#haze-book --print-out-paths --no-link) dist - - uses: https://codeberg.org/git-pages/action@v2 - with: - site: https://${{ env.SITE_ORIGIN }}${{ env.SITE_BASEPATH }} - token: ${{ forge.token }} - source: dist/ diff --git a/.gitignore b/.gitignore index 87a4f44..512d7c7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,3 @@ .direnv .env result -.vale/* -!.vale/config diff --git a/.vale.ini b/.vale.ini deleted file mode 100644 index 1f31616..0000000 --- a/.vale.ini +++ /dev/null @@ -1,13 +0,0 @@ -StylesPath = .vale -MinAlertLevel = suggestion -Vocab = haze - -[formats] -mdx = md - -[*] -BasedOnStyles = Vale - -[*.{md,mdx}] -BasedOnStyles = Vale -Microsoft.Contractions = NO diff --git a/.vale/config/vocabularies/haze/accept.txt b/.vale/config/vocabularies/haze/accept.txt deleted file mode 100644 index 925d628..0000000 --- a/.vale/config/vocabularies/haze/accept.txt +++ /dev/null @@ -1,26 +0,0 @@ -Nextcloud -ownCloud -appstore -Authentik -Caddy -cron -FrankenPHP -PHPUnit -mdBook -boolean -Codeberg -Kaspersky -Redis -Podman -Xdebug -occ -dnsmasq -notify_push -sharding -tokio -worktree -worktrees -config -stdin -stdout -direnv diff --git a/CHANGELOG.md b/CHANGELOG.md index 4de8b07..8c1b845 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,9 @@ -## 2.4.2 - -- Support having a `#!` inside a haze script to specify the interpreter to use -- Add WebUI for accessing the database. -- Allow opening services with `haze open [service]` - ## 2.4.1 - Show database location on start - Direct TLS listening support for the proxy -- Set `SERVER_NAME` for FrankenPHP -- Expose Caddy admin endpoints when using FrankenPHP +- Set `SERVER_NAME` for frankenphp +- Expose Caddy admin endpoints when using frankenphp - Use cron for background jobs ## 2.4.0 @@ -18,34 +12,34 @@ - Add option to start an instance with a detached worktree - Add "haze scripts" that bundle a setup configuration and script to run in the instance -- Add option to specify PHPUnit version when running tests -- Add SAML service using Authentik -- Add OIDC service using Authentik -- Add SCIM service using Authentik +- Allow specifying phpunit version when running tests +- Add SAML service using authentik +- Add OIDC service using authentik +- Add SCIM service using authentik - Fix using a single word as instance name - Fixed cleanup not removing some cache files -- Remove memory and CPU limits from containers +- Remove memory and cpu limits from containers - Fix max upload size not being set correctly ## 2.3.0 -- Add option to `exec` in service containers -- Add SFTP with key authentication service -- Fix MySQL 8 support +- Allow execing into service containers +- Add sftp with key authentication service +- Fix mysql 8 support ## 2.2.2 - parallelize `git pull` - add webhook tester -- automatically configure LDAP when enabled -- improve compatibility with integration tests +- automatically configure ldap when enabled +- improve compatibility with intergration tests - add `php-imagick` module ## 2.1.1 -- Add basic [FrankenPHP](https://github.com/php/frankenphp) support +- Add basic [frankenphp](https://github.com/php/frankenphp) support - Allow running integration tests from (some) apps -- Support federation with proxy and using 127.0.0.1 as proxy IP +- Support federation with proxy and using 127.0.0.1 as proxy ip - Fix office not working - Warn when using out-of-date images. @@ -55,8 +49,8 @@ - Faster stopping of instances, by @provokateurin - Support extra app directories - Enable appstore -- Allow setting configuration options pre-setup +- Allow setting config options pre-setup - Improved access to service containers - Add S3 with TLS options -- Updated PHP 8.0 and 8.1 images -- Add PHP 8.5 support +- Updated php 8.0 and 8.1 images +- Add php 8.5 support diff --git a/Cargo.lock b/Cargo.lock index bbaba4a..97e3e78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -899,7 +899,7 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "haze" -version = "2.4.2" +version = "2.4.1" dependencies = [ "async-trait", "atty", diff --git a/Cargo.toml b/Cargo.toml index 3968b51..c21a0a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "haze" -version = "2.4.2" +version = "2.4.1" edition = "2024" description = "Easy setup and management of Nextcloud test instances using docker" repository = "https://codeberg.org/icewind/haze" diff --git a/DEVELOPING.md b/DEVELOPING.md deleted file mode 100644 index d7f9f65..0000000 --- a/DEVELOPING.md +++ /dev/null @@ -1,80 +0,0 @@ -# Developing - -## Setup - -### Nix based development environment - -The recommended way to setup the development environment is to use -[Lix](https://lix.systems/install/)/[Nix](https://nixos.org/download/). - -Once installed, you can enter the development shell using `nix develop` or, -setup [direnv](https://direnv.net/) to automatically enter the development shell -when you enter the folder. - -The development shell provides all necessary tooling to develop haze. - -### Without Nix - -The fooling tools need to be setup to develop without using Nix to manage the -development setup: - -- [`cargo`](https://doc.rust-lang.org/cargo/getting-started/installation.html) - - For building the program -- [`mdbook`](https://github.com/rust-lang/mdBook) - For building the - documentation -- [`prettier`](https://prettier.io/) - For formatting the non-rust files -- [`vale`](https://vale.sh/) - For checking documentation - -## Building - -```bash -cargo build -``` - -The binary can then be found at `target/debug/haze`. - -## Formatting - -### With Nix - -```bash -nix fmt -``` - -### Without Nix - -#### Rust code - -```bash -cargo fmt -``` - -#### Documentation - -```bash -prettier --write '**/*.md' -``` - -### Building documentation - -```bash -mdbook serve -``` - -### Checking documentation - -```bash -vale src/ book/src/ book/README.md *.md -``` - -### Run all checks - -```bash -nix flake check -``` - -### Building docker images - -```bash -nix run .#'"haze-image-php-8.4"'.copyToDockerDaemon -``` diff --git a/README.md b/README.md index 33aee03..d66688b 100644 --- a/README.md +++ b/README.md @@ -7,35 +7,528 @@ Easy setup and management of Nextcloud test instances using docker ## What `haze` provides an easy way to set up Nextcloud test instances with a choice of -PHP version, database server, optional s3 or LDAP setup and more. +php version, database server, optional s3 or ldap setup and more. -## Documentation +## Setup -Documentation for haze can be found in the -[book](https://icewind.codeberg.page/haze/) +### Requirements -## Quickstart +- Docker + +### Installation - Grab a binary from the [Codeberg releases](https://codeberg.org/icewind/haze/releases) and place it in your `$PATH` -- Create a file `~/.config/haze/haze.toml` with the following content: +### Config - ```toml - sources_root = "/path/to/nextcloud/sources" - ``` +Create a file `~/.config/haze/haze.toml` with the following options: -- Start a basic Nextcloud instance: +```toml +sources_root = "/path/to/nextcloud/sources" +``` + +See the [configuration section](#configuration) for more options. + +### Quick examples + +- Start a Nextcloud instance with `postgresql`, and `s3` primary storage: ```bash - haze start + haze start pgsql s3 ``` -- Navigate to the address that is provided in the output. +- Start a Nextcloud instance with `sqlite`, `php 8.3` and an `smb` external + storage: -See the [book](https://icewind.codeberg.page/haze/) for more details. + ```bash + haze start 8.3 smb + ``` -## Developing +- Run specific units test against an `oracle` database + ```bash + haze test oracle apps/dav/tests/unit/Connector/Sabre + ``` -See [DEVELOPING.md](./DEVELOPING.md) for information on how to work on `haze`. +## Managing instances + +#### Start an instance + +```bash +haze start [--name ] [--detach] [database] [php-version] [services] [vX.Y.Z] +``` + +Where `database` is one of `sqlite`, `mysql`, `mariadb`, `pgsql` or `oracle` +with an optional version (e.g. `pgsql:12`), defaults to `sqlite`. And +`php-version` is one of `8.0`, `8.1`, `8.2`, `8.3`, `8.4` or `8.5`, defaults to +the maximum version support by the current Nextcloud version. + +You can specify a version number (e.g. `v32.0.2`) to use the sources from a +release instead of using the local sources. + +Use `--name ` to give the instance a specific name instead of a randomly +generated one. For example: + +Use `--detach` to give the instance +[its own sources](#instances-with-their-own-sources) instead of sharing +`sources_root` with all other instances. + +Additionally, you can use the following options when starting an instance: + +- `s3`: set up an S3 server and configure to Nextcloud to use it as primary + storage. + - `s3s`: enable TLS for the S3 setup. + - `s3mb`: enable multi-bucket S3 setup. + - `s3m`: enable multi-instance S3 setup. +- `ldap`: set up an LDAP server. +- `saml`: set up authentik as a SAML IDP. +- `oidc`: set up authentik as an OIDC IDP. +- `scim`: set up authentik as a SCIM server. +- `office`: set up a Nextcloud Office server. +- `onlyoffice` setup an onlyoffice document server. +- `push` set up [client push](https://github.com/nextcloud/notify_push). +- `smb`: set up a samba server for external storage use. +- `dav`: set up a WebDAV server for external storage use. +- `sftp`: set up a SFTP server for external storage use. +- `sftp-key`: set up a SFTP server for external storage use with public key + authentication. +- `kaspersky`: set up a kaspersky scan engine server in http mode. ( Requires + [manually setting up the image](https://github.com/icewind1991/kaspersky-docker)) +- `kaspersky-icap`: setup a kaspersky scan engine server in ICAP mode. +- `clamav`: set up a local clam av scanner in executable mode. +- `clamav-socket`: set up a clam av scanner in socket mode. +- `clamav-icap`: set up a clam av scanner in ICAP mode. +- `clamav-icap-tls`: set up a clam av scanner in ICAP mode with TLS encryption. +- `oc`: start an ownCloud instance in the same network. +- `imaginary`: start an Imaginary service and configure it for preview + generation. +- `mail`: start a [smtp4dev](https://github.com/rnwood/smtp4dev) server and + configure it the mail server. +- `webhook` start a + [webhook tester](https://github.com/tarampampam/webhook-tester) +- `redis`: start a separate container for redis. +- `redis-tls`: connect to redis over TLS. +- ``: by specifying the path to an app package this package + will be extracted into the apps. directory of the new instance (overwriting + any existing app code). This can be used to quickly test a packaged app. +- The name of any configured preset. + +#### Run tests in a new instance + +```bash +haze test [database] [php-version] [phpunit version] [path] +``` + +Where `path` is a file or folder to run PHPUnit in, relative to the sources +root. + +### List running instances + +```bash +haze +``` + +or + +```bash +haze list +``` + +#### Remove all running instances + +```bash +haze clean +``` + +### Instances with their own sources + +By default every instance shares the sources configured as `sources_root`, so +all instances always run the same code. An instance started with `--detach` gets +its own `git worktree` of `sources_root` instead, created in `worktree_dir`, +which lets you run several instances on different branches at the same time. + +```bash +haze start --name my-fix --detach +``` + +The worktree is checked out with a detached head. Every app in one of the +`app_directories` that gets enabled during setup receives its own worktree as +well, other apps keep running from the shared app directory. + +The worktrees are removed together with the instance, by `haze stop` or +`haze clean`. + +## Controlling running instances + +The following commands run against the most recently started instance and allow +optionally providing a `match` to select a specific instance by its name. + +#### Open an instance + +```bash +haze [match] open +``` + +#### Open the database of an instance + +```bash +haze [match] db +``` + +#### Execute a command on an instance + +```bash +haze [match] [service] [cmd] +``` + +If no `cmd` is specified it will launch `bash` + +If a service name or `db` is provided, the command will be in the container of +the service or database. + +#### Create a new instance and run a command + +```bash +haze [match] shell [cmd] +``` + +If no `cmd` is specified it will launch `bash` + +#### Execute an occ command on an instance + +```bash +haze [match] occ [cmd] +``` + +#### Connect to the database on an instance + +```bash +haze [match] db +``` + +#### Show the logs of an instance + +```bash +haze [match] logs +``` + +#### Stop an instance + +```bash +haze [match] stop +``` + +#### Pin an instance + +```bash +haze [match] pin +``` + +Pinned instances will not be removed by `haze clean`. + +#### Unpin an instance + +```bash +haze [match] unpin +``` + +#### Run a command with instance environment variables set + +```bash +haze [match] env [args] +``` + +Runs the provided command with `NEXTCLOUD_URL`, `DATABASE_URL` and `REDIS_URL` +environment variables set for the matched instance. + +This is intended to run a local +[push daemon](https://github.com/nextcloud/notify_push) against an instance. + +#### Update the container images + +```bash +haze update +``` + +#### Edit a file in an instance with the local $EDITOR + +```bash +haze [match] edit +``` + +#### Reload the php config of an instance + +```bash +haze [match] reload +``` + +The php configuration can edit changed with `haze edit /config/php.ini` + +#### Checkout a branch for all local apps + +```bash +haze git checkout [branch] +``` + +Checks out the branch in all git repositories within the apps folder. + +Defaults to the branch matching the current checked out server versions (e.g. +`master` or `stable33`). + +`master` and `main` can be used interchangeably. + +#### Pull remote changes for all local apps + +```bash +haze git pull +``` + +Performs a pull in all git repositories within the apps folder. + +## Federation + +Multiple instances can reach each other by using their instance name as domain +name to allow for testing federation between instances. Alternatively, you can +set up the haze proxy and the proxied domains to get https support between +instances. + +## Proxy + +By default, instances can be accessed by their IP. In order to get more +memorable URLs and allow supporting https, haze comes with a builtin reverse +proxy to allow using a wildcard domain. + +### DNS Setup + +#### Requirements + +- A domain name you can set wildcard DNS records for +- A reverse proxy like Nginx or Apache +- (optionally) a wildcard ssl certificate (can be acquiring using letsencrypt + and dns verification) + +#### Steps + +- Set a DNS record for `*.haze.example.com` and `haze.example.com` pointing to + your development machine. +- Set the `proxy` configuration with your domain and desired listen endpoint. +- Set up a service to run `haze proxy` in the background as your own user. A + systemd user service is recommended (see [haze.service](./haze.service) for an + example). +- Configure your reverse proxy of choice to proxy `*.haze.example.com` and + `haze.example.com` to the proxy's listen endpoint +- (optional) acquire a wildcard ssl certificate for your domain and set your + reverse proxy to use it. This will be highly dependent on your DNS provider, + [this](https://community.letsencrypt.org/t/dns-providers-who-easily-integrate-with-lets-encrypt-dns-validation/86438) + lists some DNS providers and supported ACME clients. + +### Local Setup + +- Setup `dnsmasq` to resolve `*.haze.test` to your development machine. +- Generate a wildcard ssl certificate for `*.haze.test` using `mkcert`: + +```bash +# Generate local wildcard certificate +mkcert -cert-file haze.test.crt -key-file haze.test.key '*.haze.test' +``` + +- Set up a service to run `haze proxy` in the background as your own user. A + systemd user service is recommended (see [haze.service](./haze.service) for an + example). +- Either point haze at the certificate directly: + +```toml +[proxy] +address = "haze.test" +https = true +listen = "127.0.0.1:443" +cert = "/haze.test.crt" +key = "/haze.test.key" +``` + +Binding to port 443 as a regular user requires either +`sudo setcap cap_net_bind_service=+ep $(which haze)` or +`sysctl net.ipv4.ip_unprivileged_port_start=443`. + +- Or, if you already have another web server, setup it up to proxy `*.haze.test` + and `haze.test` to the `haze proxy`'s socket. Example for Nginx: + +```nginx +upstream haze-handler { + server unix:/run/haze/haze.sock; +} + +server { + listen 80; + listen 443 ssl; + http2 on; + server_name *.haze.test; + + ssl_certificate /haze.test.crt; + ssl_certificate_key /haze.test.key; + + location / { + proxy_pass http://haze-handler; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` + +### Usage + +When the proxy is configured, generated URLs for the instances will use a +subdomain of the configured domain, e.g. the `rolling-bees` instance will be +available at `rolling-bees.haze.example.com`. Additionally, `haze.example.com` +will automatically point to the last created instance. + +Additionally, the proxy allows access to the server containers trough either +`-.haze.example.com` for a specific instance, or +`.haze.example.com` for the last created instance. For example +`rolling-bees-mail.haze.example.com` will give access to the smtp4dev web +interface of the `rolling-bees` instance. + +## Haze scripts + +Haze scripts combine a set of instance options and a script to run in the +instance. + +Haze scripts are intended to way to create automated ways of running more +complex tests are setting up more complex instances. + +A script contains of 3 paths + +1. An optional shebang line setting `haze` as the interpreter, e.g. + `#! /usr/bin/env -S haze script` +2. A shebang line setting the options for the instance creation as the + interpreter, e.g. `#! haze shell pgsql s3` +3. The rest that is ran as a script inside the created instance. + +The first sheband is set, the script can be ran directly. Else it needs to be +run with `haze script [path-to-script]`. + +For example, the following script will create an instance with `postgresql` and +`s3` primary storage. Then creates a new file, gets the file id, read the object +of the file from S3, validate that it contains the expected contents, and +cleanup the instance. + +```bash +#! #! /usr/bin/env -S haze script +#! haze shell pgsql s3 + +echo 'test' | occ file:put '-' /admin/files/test.txt +FILE_ID=$(occ info:file /admin/files/test.txt | grep fileid: | grep -oE '[0-9]+') +OBJECT_CONTENTS=$(occ file:object:get urn:oid:$FILE_ID -) +if [ "$OBJECT_CONTENTS" == 'test' ]; then + echo "object contains expected contents" +else + echo "object does not contains expected contents!" +fi +``` + +### Script modes + +Scripts can be either `shell` or `start` scripts. + +`shell` scripts will automatically remove the created instance once the script +is done, just like `haze shell` will. The intended use case for this is +performing some tests in the created instance without leaving state behind. + +`start` scripts on the other hand will keep the instance, like `haze start` +will. The intended use case for this is creating more complex instances without +having to configure a dedicated preset. + +The script mode is determined based on the shebang line. The mode set in a +script can be overriden by running the script with +`haze script [shell|start] path-to-script.sh`. + +## Nushell scripts + +By default, the script contents are executed as a `bash` script, you can instead +execute the script as a `nushell` script by using `.nu` as the file extention. + +## Configuration + +Configuration is loaded from `~/.config/haze/haze.toml` and has the following +options + +```toml +sources_root = "/path/to/sources" # path of the nextcloud sources. required +app_directories = ["/path/to/sources/more_app"] # paths to additional app directories. +work_dir = "/path/to/temp/dir" # path to temporary directory. optional, defaults to "/tmp/haze" +worktree_dir = "/path/to/worktrees" # where to create the worktrees of detached instances. optional, defaults to "/worktrees" + +[auto_setup] # optional +enabled = false # whether or not to automatically install nextcloud on `haze start`. enabled by default +username = "foo" # username for admin user during auto setup. optional, defaults to "admin" +password = "bar" # password for admin user during auto setup. optional, defaults to "admin" +enable_apps = ["files_external"] # apps to enable after setup, defaults to [] +disable_apps = ["contacts"] # apps to disable after setup, defaults to [] +post_setup = [# commands to execute after setup, defaults to [] + "occ group:add test", +] +config = { "foo" = "bar" } # configuration options to set before install + +[[volume]] # optional +source = "/tmp/haze-shared" +target = "/shared" +create = true + +[[volume]] +source = "/home/me/Downloads" +target = "/Downloads" +read_only = true + +[proxy] # optional +address = "haze.example.com" # base domain +https = true # Whether the instances are reachable over https +listen = "/run/haze/haze.sock" # either a unix socket path +#listen = "127.0.0.1:8080" # or a socket address +cert = "/path/to/haze.test.crt" # optional - PEM encoded certificate chain +key = "/path/to/haze.test.key" # optional - PEM encoded private key + +# presets allow for easy usage of commonly used setups +[[preset]] +name = "groupfolders" # name of the preset +apps = ["groupfolders"] # app to enable +commands = ["occ groupfolders:create gf", "occ groupfolders:group 1 admin read write share delete"] # commands to run post-setup +``` + +## Xdebug + +Haze Xdebug is running in a docker container, you need to tell you IDE how to +properly map the path. The IDE debugger config usually looks like: + +```json + { + "label": "PHP: Debug server within docker", + "adapter": "Xdebug", + "request": "launch", + "port": 9003, + "pathMappings": { + "/var/www/html": "", + "/var/www/html/apps-extra": "", + }, + }, +``` + +This would have to be adapted for detached instances. + +To enable Xdebug for all requests: + +- Uncomment the lines in `haze edit /config/php.ini` +- Then run `haze reload ` + +## FrankenPHP (experimental) + +You can have Nextcloud run on FrankenPHP. + +Caddy's admin metrics are then accessible through +`http://-franken-php.haze.test`. With ember you can run: + +```bash +ember --addr http://-franken-php.haze.test +``` diff --git a/book/.gitignore b/book/.gitignore deleted file mode 100644 index e9c0728..0000000 --- a/book/.gitignore +++ /dev/null @@ -1 +0,0 @@ -book \ No newline at end of file diff --git a/book/README.md b/book/README.md deleted file mode 100644 index 0f73f56..0000000 --- a/book/README.md +++ /dev/null @@ -1,9 +0,0 @@ -## Build requirements - -- [mdBook](https://github.com/rust-lang/mdBook) - -## Development - -``` -mdbook serve -``` diff --git a/book/book.toml b/book/book.toml deleted file mode 100644 index c2ab188..0000000 --- a/book/book.toml +++ /dev/null @@ -1,4 +0,0 @@ -[book] -title = "Haze" -authors = ["Robin Appelman"] -language = "en" diff --git a/book/src/README.md b/book/src/README.md deleted file mode 100644 index e5ee49c..0000000 --- a/book/src/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Haze - -Hazy with a chance of clouds. - -`haze` is a tool that provides an easy way to set up Nextcloud test instances -with a choice of PHP version, database server, optional s3 or LDAP setup and -much more. - -## Quickstart - -- Grab a binary from the - [Codeberg releases](https://codeberg.org/icewind/haze/releases) and place it - in your `$PATH` - -- Create a file `~/.config/haze/haze.toml` with the following content: - - ```toml - sources_root = "/path/to/nextcloud/sources" - ``` - -- Start a basic Nextcloud instance: - - ```bash - haze start - ``` - -- Navigate to the address that is provided in the output. - -See the [setup](./setup.html), [configuration](./configuration.html) and -[usage](./usage.html) for a more details. diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md deleted file mode 100644 index b201bc6..0000000 --- a/book/src/SUMMARY.md +++ /dev/null @@ -1,16 +0,0 @@ -# Summary - -[Introduction](README.md) - -- [Setup](setup.md) -- [Usage](usage.md) -- [Configuration](configuration.md) -- [Proxy](proxy/README.md) - - [DNS](proxy/dns.md) - - [HTTPS](proxy/https.md) -- [Services](services.md) -- [Database](database.md) -- [Federation](federation.md) -- [Haze scripts](scripts.md) -- [Xdebug](xdebug.md) -- [FrankenPHP (experimental)](frankenphp.md) diff --git a/book/src/configuration.md b/book/src/configuration.md deleted file mode 100644 index 8ec8d8a..0000000 --- a/book/src/configuration.md +++ /dev/null @@ -1,271 +0,0 @@ -# Configuration - -Configuration is loaded from `~/.config/haze/haze.toml`. - -The minimum required configuration needed to get started is just the -`sources_root` options. - -The full list of supported options is: - -### `sources_root` - -The local path of the Nextcloud sources. This options is **required**. - -Type: string - -### `app_directories` - -A list of additional app directory paths to look for apps into - -Default `[]` - -Type: list of strings - -### `work_dir` - -The location where haze keeps it's temporary files and caches - -Default: `/tmp/haze` - -Type: string - -### `worktree_dir` - -The location to store git worktrees when using instances with detached sources. - -Default: `/worktrees` - -Type: string - -## `auto_setup` - -Options for automatically setting up the newly created Nextcloud instance. - -Examples: - -```toml -[auto_setup] -username = "foo" -password = "bar" -enable_apps = ["files_external"] -disable_apps = ["contacts"] -post_setup = [ - "occ group:add test", -] -config = { "enforce_theme" = "dark" } -``` - -```toml -[auto_setup] -enabled = false -``` - -### `auto_setup.enabled` - -Whether to automatically setup Nextcloud inside a created instance. - -Default: `true` - -Type: boolean - -### `auto_setup.username` - -The username to use for the admin account during auto setup. - -Default: `admin` - -Type: string - -### `auto_setup.password` - -The password to use for the admin account during auto setup. - -Default: `admin` - -Type: string - -### `auto_setup.enabled_apps` - -Extra apps to enable after auto setup - -Default: `[]` - -Type: list of strings - -### `auto_setup.disabled_apps` - -Apps to disabled after auto setup - -Default: `[]` - -Type: list of strings - -### `auto_setup.post_setup` - -Commands to execute after auto setup - -Default: `[]` - -Type: list of strings - -### `auto_setup.config` - -System configuration options to set before auto setup - -Default: `{}` - -Type: Object - -## `volume` - -Additional files or directories to bind-mount into instances. - -Any number of volume options can be configured - -Examples: - -```toml -[[volume]] -source = "/tmp/haze-shared" -target = "/shared" -create = true -``` - -```toml -[[volume]] -source = "/home/me/Downloads" -target = "/Downloads" -read_only = true -``` - -### `volume.source` - -The source path on the host - -Type: string - -### `volume.target` - -The target path inside the container - -Type: string - -### `volume.create` - -Create the source directory on the host if it doesn't exist already. - -Default: `false` - -Type: boolean - -### `volume.read_only` - -Whether to mount the file or directory as read only - -Default: `false` - -Type: `boolean` - -### preset - -Configured presets that can be used when creating instances. - -Any number of presets can be configured. - -Example: - -```toml -[[preset]] -name = "groupfolders" -apps = ["groupfolders"] -commands = [ - "occ groupfolders:create gf", - "occ groupfolders:group 1 admin read write share delete" -] -``` - -### `preset.name` - -The name of the preset, this is used when creating instances to select the -preset. - -Type: string without whitespace - -### `preset.apps` - -A list of apps to enable when the preset is used. - -Default: `[]` - -Type: list of strings - -### `preset.commands` - -A list of commands to run post-setup when the preset is used. - -Default: `[]` - -Type: list of strings - -## `proxy` - -Configuration for the haze proxy. Only required when using the proxy. - -```toml -[proxy] -address = "haze.example.com" -listen = "/run/haze/haze.sock" -https = true -cert = "/path/to/haze.test.crt" -key = "/path/to/haze.test.key" -``` - -### `proxy.listen` - -The IP and port or Unix socket for the proxy to listen on. - -**Required** when the proxy is enabled. - -Type: string - -Examples: - -```toml -listen = "/run/haze/haze.sock" -``` - -```toml -listen = "127.0.0.1:8080" -``` - -### `proxy.address` - -The base domain the proxy is accessible on. - -**Required** if the proxy is enabled. - -Type: string - -### `proxy.https` - -Whether to enable built-in HTTPS support for the proxy. - -Default: `false` - -Type: boolean - -### `proxy.cert` - -The path of the PEM encoded certificate chain to use for HTTPS. - -**Required** if HTTPS is enabled for the proxy. - -Type: string. - -### `proxy.cert` - -The path of the PEM encoded private key to use for HTTPS. - -**Required** if HTTPS is enabled for the proxy. - -Type: string. diff --git a/book/src/database.md b/book/src/database.md deleted file mode 100644 index bf96761..0000000 --- a/book/src/database.md +++ /dev/null @@ -1,33 +0,0 @@ -# Database - -There are 3 ways of accessing the database of a haze instance. - -## CLI - -```bash -haze [filter] db [query] -``` - -Opens the command line client for the database. If a query is specified, it will -be executed. If no query is specified, an interactive shell will be started. - -Example - -``` -haze db 'select fileid, storage, path from oc_filecache' -``` - -## WebUI - -A web UI (based on [DbGate](https://www.dbgate.io/)) is available with every -instance. - -It can be accessed by using `haze open db`, or, if the proxy is setup at -`db.haze.example.com`. - -## External tools - -For usage with external tools, the database connection URL is printed when the -instance is created. - -Details on how to use the connection URL is dependent on the external tool. diff --git a/book/src/federation.md b/book/src/federation.md deleted file mode 100644 index 23bab7a..0000000 --- a/book/src/federation.md +++ /dev/null @@ -1,13 +0,0 @@ -# Federation - -Multiple instances can reach each other by using their instance name as domain -name to allow for testing federation between instances. - -For example, if you have a `rover-beavers` and `splendid-couch` instance, you -can create a federated share from the `rover-beavers` instance to -`http://admin@splendid-couch`. - -If the proxy is setup with HTTP, you can use HTTP between the instances by using -the full proxy URLs. - -For example sharing to `admin@splendid-couch.haze.example.com`. diff --git a/book/src/frankenphp.md b/book/src/frankenphp.md deleted file mode 100644 index 92f675b..0000000 --- a/book/src/frankenphp.md +++ /dev/null @@ -1,11 +0,0 @@ -# FrankenPHP (experimental) - -You can have Nextcloud run on FrankenPHP by starting the instance with the -`franken-php` option. - -Caddy's admin metrics are then accessible through -`http://-franken-php.haze.test`. With ember you can run: - -```bash -ember --addr http://-franken-php.haze.test -``` diff --git a/book/src/proxy/README.md b/book/src/proxy/README.md deleted file mode 100644 index edc643e..0000000 --- a/book/src/proxy/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# Proxy - -By default, instances can be accessed by their IP. In order to get more -memorable URLs and allow supporting HTTPS. haze comes with a builtin reverse -proxy to allow using a wildcard domain. - -## Setup - -- [Setup a DNS record](./dns.html) for `*.haze.example.com` and - `haze.example.com` pointing to your development machine. -- Set the `proxy` configuration with your domain and desired listen endpoint. -- Set up a service to run `haze proxy` in the background as your own user. A - SystemD user service is recommended (see - [haze.service](<[./haze.service](https://codeberg.org/icewind/haze/src/branch/main/haze.service)>) - for an example). -- If you're already running a reverse proxy, configure your reverse proxy of - choice to proxy `*.haze.example.com` and `haze.example.com` to the proxy's - listen endpoint. -- (Optionally) [setup HTTPS](./https.html) for the proxy. - -### Configuration - -Add the following configuration to the `haze.toml` configuration file: - -```toml -[proxy] -address = "haze.example.com" # the base domain for the proxy to use -listen = "127.0.0.1:8080" # the port+ip to listen on -# listen = "/var/run/haze/haze.sock" # or a unix socket path -``` - -### Without a reverse proxy - -If you have no other http(s) servers on your development machine, you can setup -things without a reverse proxy. - -Simply configure the proxy to listen on port `80` (or `443` when using HTTPS). - -Binding to port 80 or443 as a regular user requires either giving the haze -binary the `net_bind_service` capability with -`sudo setcap cap_net_bind_service=+ep $(which haze)` (this will have to be done -every time your upgrade haze) or configure your system to allow unprivileged -users to bind on the low port numbers. Using -`sysctl net.ipv4.ip_unprivileged_port_start=80` and writing - -``` -net.ipv4.ip_unprivileged_port_start=80 -``` - -to `/etc/sysctl.d/bind.conf` to make it persistent across reboot. - -### With a reverse proxy - -If you're already have other http(s) services listening on your machine, you'll -probably want to setup a reverse proxy to allow them to all be served on your -machine. - -The setup for this will depend on your reverse proxy of the choice, the -following example configuration is for `nginx`. - -```nginx -upstream haze-handler { - server unix:/var/run/haze/haze.sock; -} - -server { - listen 80; - server_name *.haze.example.com; - - location / { - proxy_pass http://haze-handler; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -``` - -## Usage - -When the proxy is configured, generated URLs for the instances will use a -subdomain of the configured domain, e.g. the `rolling-bees` instance will be -available at `rolling-bees.haze.example.com`. Additionally, `haze.example.com` -will automatically point to the last created instance. - -Additionally, the proxy allows access to the service containers trough either -`-.haze.example.com` for a specific instance, or -`.haze.example.com` for the last created instance. For example -`rolling-bees-mail.haze.example.com` will give access to the smtp4dev web -interface of the `rolling-bees` instance. diff --git a/book/src/proxy/dns.md b/book/src/proxy/dns.md deleted file mode 100644 index eec3413..0000000 --- a/book/src/proxy/dns.md +++ /dev/null @@ -1,27 +0,0 @@ -# DNS - -Since the domain name used for the instance is dynamic, a wildcard DNS record is -required. - -## With your domain's DNS provider - -If you own a domain you would like to use, you can create a wildcard domain -within the DNS settings of your DNS provider. For example creating a record an -`A` record for `*.haze.example.com` with a value of `127.0.0.1` and a similar -one for `haze.example.com`. - -## With a local dnsmasq - -If you do not own a "real" domain for using with haze, you can setup `dnsmasq` -locally to achieve the same goal instead. - -How to install and enable `dnsmasq` will depend on your Linux distribution of -choice and should be documented by it's documentation. - -Once setup, a configuration line like - -``` -address=/haze.local/172.0.0.1 -``` - -should be enough to point `haze.local` and `*.haze.local` to your local host. diff --git a/book/src/proxy/https.md b/book/src/proxy/https.md deleted file mode 100644 index 6369030..0000000 --- a/book/src/proxy/https.md +++ /dev/null @@ -1,78 +0,0 @@ -# HTTPS - -The proxy can be setup to enable using HTTPS to access the running instances. -Besides the warm and fuzzy feeling of knowing that nobody can snoop on the -traffic that is happening completely local inside your machine. Accessing the -page over HTTPS is required for some JavaScript features (such as service -workers), as they are only available in "secure contexts". - -## Getting a wildcard certificate - -Since the domain name used for the instance is dynamic, a wildcard certificate -is required. - -## Let's encrypt - -Let's encrypt allows getting trusted wildcard certificates for free if you can -use DNS validation. - -How to setup DNS validation will depend on the specifics of the DNS provider and -ACME client. -[This](https://community.letsencrypt.org/t/dns-providers-who-easily-integrate-with-lets-encrypt-dns-validation/86438) -lists some DNS providers and supported ACME clients. - -## Self signed - -You can also create a self-signed wildcard certificate using a tool like -`mkcert`. This certificate will not be trusted by your browser and tools like -curl, but you can add manually add it to the trusted certificates on your -system, or bypass the certificates warning in the browser/curl every time. - -```bash -# Generate local wildcard certificate -mkcert -cert-file haze.example.com.crt -key-file haze.example.com.key '*.haze.example.com' -``` - -## Using the certificate - -### Without reverse proxy - -The haze proxy can serve over HTTPS directly, to enable that add the following -to the `[proxy]` section of your `haze.toml`. - -```toml -https = true -cert = "/path/to/haze.example.com.crt" -key = "/path/to/haze.example.com.key" -``` - -You might also want to change the port it's listening on to `443`. - -### With a reverse proxy - -This depends on what reverse proxy you have setup. The following example is for -`nginx`. - -```nginx -upstream haze-handler { - server unix:/run/haze/haze.sock; -} - -server { - listen 80; - listen 443 ssl; - http2 on; - server_name *.haze.example.com; - - ssl_certificate /haze.example.com.crt; - ssl_certificate_key /haze.example.com.key; - - location / { - proxy_pass http://haze-handler; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} -``` diff --git a/book/src/scripts.md b/book/src/scripts.md deleted file mode 100644 index b32c3c8..0000000 --- a/book/src/scripts.md +++ /dev/null @@ -1,72 +0,0 @@ -# Haze scripts - -Haze scripts combine a set of instance options and a script to run in the -instance. - -Haze scripts are intended to way to create automated ways of running more -complex tests are setting up more complex instances. - -A script contains of 3 paths - -1. An optional shebang line setting `haze` as the interpreter, e.g. - `#! /usr/bin/env -S haze script` -2. A shebang line setting the options for the instance creation as the - interpreter, e.g. `#! haze shell pgsql s3` -3. The rest that is ran as a script inside the created instance. - -The first shebang is set, the script can be ran directly. Else it needs to be -run with `haze script [path-to-script]`. - -For example, the following script will create an instance with `postgresql` and -`s3` primary storage. Then creates a new file, gets the file id, read the object -of the file from S3, validate that it contains the expected contents, and -cleanup the instance. - -```bash -#! #! /usr/bin/env -S haze script -#! haze shell pgsql s3 - -echo 'test' | occ file:put '-' /admin/files/test.txt -FILE_ID=$(occ info:file /admin/files/test.txt | grep fileid: | grep -oE '[0-9]+') -OBJECT_CONTENTS=$(occ file:object:get urn:oid:$FILE_ID -) -if [ "$OBJECT_CONTENTS" == 'test' ]; then - echo "object contains expected contents" -else - echo "object does not contains expected contents!" -fi -``` - -### Script modes - -Scripts can be either `shell` or `start` scripts. - -`shell` scripts will automatically remove the created instance once the script -is done, just like `haze shell` will. The intended use case for this is -performing some tests in the created instance without leaving state behind. - -`start` scripts on the other hand will keep the instance, like `haze start` -will. The intended use case for this is creating more complex instances without -having to configure a dedicated preset. - -The script mode is determined based on the shebang line. The mode set in a -script can be overridden by running the script with -`haze script [shell|start] path-to-script.sh`. - -## Using different interpreters - -By default, the script contents are executed as either `bash` script, or `nu` -script based on the extension. - -You can overwrite the interpreter used to execute the script by adding an extra -`#!` line to the script, for example: - -```bash -#! /usr/bin/env -S haze script -#! haze shell pgsql s3 -#! php -f -`: by specifying the path to an app package this package - will be extracted into the apps. directory of the new instance (overwriting - any existing app code). This can be used to quickly test a packaged app. -- The name of any configured preset. diff --git a/book/src/setup.md b/book/src/setup.md deleted file mode 100644 index 420eaee..0000000 --- a/book/src/setup.md +++ /dev/null @@ -1,36 +0,0 @@ -# Setup - -## Requirements - -- Docker - -haze is built around docker containers and manages containers using the docker -socket. Using Podman with docker compatibility might also work, but is untested. - -## Installation - -- Grab a binary from the - [Codeberg releases](https://codeberg.org/icewind/haze/releases) and place it - in your `$PATH` - -## Configuration - -Create a file `~/.config/haze/haze.toml` with the following options: - -```toml -sources_root = "/path/to/nextcloud/sources" -``` - -See the [configuration section](./configuration.html) for more options. - -## Test the Setup - -### Quick examples - -- Start a basic Nextcloud instance: - - ```bash - haze start - ``` - -- Navigate to the address that is provided in the output. diff --git a/book/src/usage.md b/book/src/usage.md deleted file mode 100644 index 063bc47..0000000 --- a/book/src/usage.md +++ /dev/null @@ -1,230 +0,0 @@ -# Usage - -## Quick examples - -- Start a Nextcloud instance with `postgresql`, and `s3` primary storage: - - ```bash - haze start pgsql s3 - ``` - -- Start a Nextcloud instance with `sqlite`, `php 8.3` and an `smb` external - storage: - - ```bash - haze start 8.3 smb - ``` - -- Run specific units test against an `oracle` database - ```bash - haze test oracle apps/dav/tests/unit/Connector/Sabre - ``` - -## Managing instances - -### Starting an instance - -```bash -haze start [--name ] [--detach] [database] [php-version] [services] [vX.Y.Z] -``` - -Where `database` is one of `sqlite`, `mysql`, `mariadb`, `pgsql` or `oracle` -with an optional version (e.g. `pgsql:12`), defaults to `sqlite`. And -`php-version` is one of `8.0`, `8.1`, `8.2`, `8.3`, `8.4` or `8.5`, defaults to -the maximum version support by the current Nextcloud version. - -You can specify a version number (e.g. `v32.0.2`) to use the sources from a -release instead of using the local sources. - -Use `--name ` to give the instance a specific name instead of a randomly -generated one. For example: - -Use `--detach` to give the instance -[its own sources](#instances-with-their-own-sources) instead of sharing -`sources_root` with all other instances. - -See the [services documentation](./services.html) for a list of available -services. - -### List running instances - -```bash -haze -``` - -or - -```bash -haze list -``` - -### Stop an instance - -```bash -haze [match] stop -``` - -### Remove all unpinned running instances - -```bash -haze clean -``` - -### Pin an instance - -```bash -haze [match] pin -``` - -Pinned instances will not be removed by `haze clean`. - -### Unpin an instance - -```bash -haze [match] unpin -``` - -## Run commands in a temporary instance - -```bash -haze shell [database] [php-version] [services] [cmd] -``` - -This will create an instance, run the provided command, and cleanup the -instance. - -If no `cmd` is specified it will launch `bash` - -## Run tests in a new instance - -```bash -haze test [database] [php-version] [services] [phpunit version] [path] -``` - -Where `path` is a file or folder to run PHPUnit in, relative to the sources -root. - -This will create a fresh instance, run the PHPUnit tests, and clean up the -created instance. - -## Interacting with running instances - -The following commands run against the most recently started instance by default -and allow optionally providing a `match` to select a specific instance by its -name. - -### Open an instance in the browser - -```bash -haze [match] open -``` - -### Execute a command on an instance - -```bash -haze [match] [service] [cmd] -``` - -If no `cmd` is specified it will launch `bash` - -If a service name or `db` is provided, the command will be in the container of -the service or database. - -If no `cmd` is specified it will launch `bash` - -### Execute an occ command on an instance - -```bash -haze [match] occ [cmd] -``` - -### Connect to the database on an instance - -```bash -haze [match] db -``` - -### Show the logs of an instance - -```bash -haze [match] logs -``` - -### Edit a file in an instance with the local $EDITOR - -```bash -haze [match] edit -``` - -Where `` is the path of a file inside the container, for example -`config/config.php`. - -### Reload the PHP configuration of an instance - -```bash -haze [match] reload -``` - -The PHP configuration can edit changed with `haze edit /config/php.ini` - -### Run a command with instance environment variables set - -```bash -haze [match] env [args] -``` - -Runs the provided command with `NEXTCLOUD_URL`, `DATABASE_URL` and `REDIS_URL` -environment variables set for the matched instance. - -This is intended to run a local -[push daemon](https://github.com/nextcloud/notify_push) against an instance. - -## Git tools - -Haze provides a couple of utilities to make working with many git repositories -for apps easier. - -### Checkout a branch for all local apps - -```bash -haze git checkout [branch] -``` - -Checks out the branch in all git repositories within the apps folder. - -Defaults to the branch matching the current checked out server versions (e.g. -`master` or `stable33`). - -`master` and `main` can be used interchangeably. - -### Pull remote changes for all local apps - -```bash -haze git pull -``` - -Performs a pull in all git repositories within the apps folder. - -## Update the container images - -```bash -haze update -``` - -## Instances with their own sources - -By default every instance shares the sources configured as `sources_root`, so -all instances always run the same code. An instance started with `--detach` gets -its own `git worktree` of `sources_root` instead, created in `worktree_dir`, -which lets you run several instances on different branches at the same time. - -```bash -haze start --name my-fix --detach -``` - -The worktree is checked out with a detached head. Every app in one of the -`app_directories` that gets enabled during setup receives its own worktree as -well, other apps keep running from the shared app directory. - -The worktrees are removed together with the instance, by `haze stop` or -`haze clean`. diff --git a/book/src/xdebug.md b/book/src/xdebug.md deleted file mode 100644 index 58079ca..0000000 --- a/book/src/xdebug.md +++ /dev/null @@ -1,36 +0,0 @@ -# Xdebug - -To use Xdebug running in a haze instance with your IDE for debugging, you need -to tell you IDE how to properly map the path from the container to the host. The -IDE debugger configuration usually looks like: - -```json - { - "label": "PHP: Debug server within docker", - "adapter": "Xdebug", - "request": "launch", - "port": 9003, - "pathMappings": { - "/var/www/html": "", - "/var/www/html/apps-extra": "", - }, - }, -``` - -This would have to be adapted for detached instances. - -## Debugging all requests - -By default, Xdebug is configured to only run for requests that containing the -trigger (e.g. from using the Xdebug browser extension, or adding -`?XDEBUG_SESSION_START=1` to the URL). - -To enable Xdebug for all requests: - -- Un-comment the lines in `haze [cloud-id] edit /config/php.ini` -- Then run `haze reload [cloud-id]` - -## Xdebug profile and trace files - -When enabling Xdebug trace or profile mode, the generated files can be found in -`//xdebug`. diff --git a/example-script.sh b/example-script.sh index 410a702..5e8a821 100755 --- a/example-script.sh +++ b/example-script.sh @@ -1,6 +1,5 @@ #! /usr/bin/env -S haze script #! haze shell pgsql s3 -#! /usr/bin/env nu echo 'test' | occ file:put '-' /admin/files/test.txt FILE_ID=$(occ info:file /admin/files/test.txt | grep fileid: | grep -oE '[0-9]+') diff --git a/flake.lock b/flake.lock index c08fe28..1603da3 100644 --- a/flake.lock +++ b/flake.lock @@ -38,11 +38,11 @@ ] }, "locked": { - "lastModified": 1790013821, - "narHash": "sha256-0OrPYAGfGF5BGPPtzEzeMNhNcvGyQBWRRfvcE0xY750=", + "lastModified": 1789408620, + "narHash": "sha256-ZymN+wNsIs0rMw9ydP5Yp5GIUKZoA8b8IDsCnz30VfE=", "owner": "nix-community", "repo": "flakelight", - "rev": "a57b0af9d0da4c5389ccf19e310daf0e5518312a", + "rev": "1d9163b9abf315129e912f3157e03b265f104838", "type": "github" }, "original": { @@ -80,11 +80,11 @@ ] }, "locked": { - "lastModified": 1790258944, - "narHash": "sha256-3bwtAiifsfGXVLIopwhJ/RNIkDABcLg6DxnlbaiE2xc=", + "lastModified": 1788758950, + "narHash": "sha256-b3zONUcYXZHeoeYwDZdSDCzMj0s6ubmD6NT3D7sS+jU=", "owner": "nlewo", "repo": "nix2container", - "rev": "08d8889b6d2528acfce6e2616b0cd41983dedb02", + "rev": "b6ac40ef110c12ab1651fce5ea563f7837236439", "type": "github" }, "original": { @@ -95,11 +95,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1790218706, - "narHash": "sha256-6e4Na3z008XpdVyOXgfasXIn1aN+z8AXFG+jXdQSyuI=", + "lastModified": 1789749394, + "narHash": "sha256-cFTsMQz8Hzn8MT49oaeLSHg62tE86NykugCWkeM2ypk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c508844df6c28fa6dabc1b6af70f3ccbd65c5201", + "rev": "cf9d2fb3e50fa1cd5114c47505ea9177f7ff5f49", "type": "github" }, "original": { @@ -110,11 +110,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1789370336, - "narHash": "sha256-6RSEDHIWQtesQKWSu5qRai8L2h4KgCgMEfJHstW99G4=", + "lastModified": 1788953386, + "narHash": "sha256-dIqD4NX3Uldk29CBqKt9dRdSxh/+Ba8lVPu2xEsLTJo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c7def046b9a883d46974757852106483d741586f", + "rev": "19a27817106f9449970edb3a398ab5349666466f", "type": "github" }, "original": { @@ -130,11 +130,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1790337121, - "narHash": "sha256-phqixEWMLjvdlUpo6uBCYbVYuGCk0FjiIoC0zPTCu3c=", + "lastModified": 1789262846, + "narHash": "sha256-IrgEYbj5LY4EskP8lvYRexPp0jI2KBy3NJZBBgSqrpg=", "owner": "fossar", "repo": "nix-phps", - "rev": "2a7ca28d4634890d2b07a63847cd6d26da9c2cb4", + "rev": "af15d3f84460bfa8cd15278795347e4661e0a199", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index d4c9127..99f9772 100644 --- a/flake.nix +++ b/flake.nix @@ -55,7 +55,6 @@ "haze-image-php-8.2" = pkgs: pkgs.haze-image-php-82; "haze-image-php-8.1" = pkgs: pkgs.haze-image-php-81; "haze-image-php-8.0" = pkgs: pkgs.haze-image-php-80; - haze-book = pkgs: pkgs.haze-book; }; tools = pkgs: @@ -64,14 +63,8 @@ bacon skopeo dive - mdbook - vale ]; - checks = { - vale = {vale, ...}: "${vale}/bin/vale src/ book/src/ book/README.md *.md"; - }; - homeModules = { default = { pkgs, diff --git a/nix/book.nix b/nix/book.nix deleted file mode 100644 index a1b1442..0000000 --- a/nix/book.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ - mdbook, - stdenv, - ... -}: -stdenv.mkDerivation { - name = "haze-book"; - src = ../book; - - nativeBuildInputs = [mdbook]; - - buildPhase = '' - mdbook build - ''; - - installPhase = '' - mkdir -p $out - cp -r book/* $out/ - ''; -} diff --git a/nix/overlay.nix b/nix/overlay.nix index 030b559..276b70e 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -6,5 +6,4 @@ final: prev: { haze-image-php-82 = final.callPackage ./image/haze.nix {php = final.php82;}; haze-image-php-81 = final.callPackage ./image/haze.nix {php = final.php81;}; haze-image-php-80 = final.callPackage ./image/haze.nix {php = final.php80;}; - haze-book = final.callPackage ./book.nix {}; } diff --git a/src/args.rs b/src/args.rs index 1f13cc2..181db11 100644 --- a/src/args.rs +++ b/src/args.rs @@ -64,7 +64,6 @@ pub enum HazeArgs { }, Open { filter: Option, - service: Option, }, Fmt { path: String, @@ -318,20 +317,7 @@ impl HazeArgs { .into_diagnostic()?, }) } - HazeCommand::Open => { - let mut args = args.peekable(); - let service = match args.peek() { - Some(arg) => Service::from_type(&[], arg.as_ref()) - .into_iter() - .filter_map(|services| services.into_iter().next()) - .next() - .inspect(|_| { - args.next(); - }), - _ => None, - }; - Ok(HazeArgs::Open { filter, service }) - } + HazeCommand::Open => Ok(HazeArgs::Open { filter }), HazeCommand::Fmt => { let path = args .next() @@ -486,7 +472,6 @@ pub enum HazeCommand { ))] Logs, /// Open an instance in the browser - #[strum(props(Args = "[service] service to open, instead of the Nextcloud instance"))] Open, /// Run code formatting from a new instance #[strum(props(Args = "[path] path to format"))] @@ -522,7 +507,7 @@ pub enum HazeCommand { /// Edit a file in the instance with $EDITOR on the host #[strum(props(Args = "[path] file to edit"))] Edit, - /// Reload the PHP configuration in the instance + /// Reload the php configuration in the instance #[strum(props( Details = "note: you can overwrite php.ini settings with haze [filter] edit /config/php.ini" ))] diff --git a/src/cloud.rs b/src/cloud.rs index 515537f..6932d00 100644 --- a/src/cloud.rs +++ b/src/cloud.rs @@ -4,9 +4,9 @@ use crate::exec::{ExitCode, exec, exec_io, exec_tty}; use crate::git::{create_worktree, find_app_repo, init_submodules, remove_worktree}; use crate::mapping::{Mapping, for_config}; use crate::php::PhpVersion; +use crate::service::Service; use crate::service::ServiceTrait; use crate::service::deduplicate_services; -use crate::service::{DbGate, Service}; use crate::sources::download_nc; use bollard::Docker; use bollard::config::NetworkCreateRequest; @@ -347,8 +347,6 @@ fn test_option_parse() { ); } -static DEFAULT_SERVICES: &[Service] = &[Service::DbGate(DbGate)]; - #[derive(Debug, Clone)] pub struct Cloud { pub id: String, @@ -541,8 +539,6 @@ impl Cloud { options .services .iter() - .filter(|service| !service.hidden()) - .chain(DEFAULT_SERVICES.iter()) .map(|service| service.spawn(docker, &id, &network, config, &options)), ) .await?; @@ -721,9 +717,9 @@ impl Cloud { .await .into_diagnostic() .wrap_err("Failed to remove work directory") - { - eprintln!("{}", e); - } + { + eprintln!("{}", e); + } remove_worktrees_for_instance(config, &self.id).await?; @@ -812,15 +808,14 @@ impl Cloud { && match filter.as_ref() { Some(filter) => cloud_id.contains(filter), None => true, + } { + let entry = containers_by_id.entry(cloud_id.to_string()).or_default(); + if labels.get("haze-type").map(String::as_str) == Some("cloud") { + entry.0 = Some(container); + } else { + entry.1.push(container) + } } - { - let entry = containers_by_id.entry(cloud_id.to_string()).or_default(); - if labels.get("haze-type").map(String::as_str) == Some("cloud") { - entry.0 = Some(container); - } else { - entry.1.push(container) - } - } } let mut sortable_containers: Vec<_> = containers_by_id @@ -974,7 +969,7 @@ impl Cloud { } pub fn services(&self) -> impl Iterator { - self.options.services.iter().chain(DEFAULT_SERVICES.iter()) + self.options.services.iter() } pub fn db(&self) -> &Database { diff --git a/src/config.rs b/src/config.rs index 64e140d..8dfd52c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -208,7 +208,7 @@ pub struct ProxyConfig { } impl ProxyConfig { - /// Get a public address for a service, either with direct IP or through the proxy + /// Get a public address for a service, either with direct ip or through the proxy pub fn addr(&self, id: &str, ip: IpAddr) -> String { let clean_id = id.strip_prefix("haze-").unwrap_or(id); match (&self.address, self.https) { diff --git a/src/database.rs b/src/database.rs index a38da00..7f943bd 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,13 +1,13 @@ use crate::config::HazeConfig; -use crate::exec::{ExitCode, exec, exec_tty}; +use crate::exec::{exec, exec_tty, ExitCode}; use crate::image::pull_image; -use bollard::Docker; use bollard::config::ContainerCreateBody; use bollard::models::{EndpointSettings, HostConfig, NetworkingConfig}; use bollard::query_parameters::CreateContainerOptions; +use bollard::Docker; use maplit::hashmap; use miette::{IntoDiagnostic, Report, Result, WrapErr}; -use std::io::{Stdout, stdout}; +use std::io::{stdout, Stdout}; use std::net::{IpAddr, Ipv4Addr}; use std::str::FromStr; use std::time::Duration; @@ -32,39 +32,6 @@ impl DatabaseFamily { pub fn name(&self) -> &'static str { self.into() } - - pub fn db_gate_driver(&self) -> &'static str { - match self { - DatabaseFamily::Mysql => "mysql@dbgate-plugin-mysql", - DatabaseFamily::MariaDB => "mariadb@dbgate-plugin-mysql", - DatabaseFamily::Postgres => "postgres@dbgate-plugin-postgres", - DatabaseFamily::Oracle => "oracle@dbgate-plugin-oracle", - DatabaseFamily::Sqlite => "sqlite@dbgate-plugin-sqlite", - } - } - - pub fn port(&self) -> u16 { - match self { - DatabaseFamily::Mysql | DatabaseFamily::MariaDB => 3306, - DatabaseFamily::Postgres => 5432, - DatabaseFamily::Oracle => 1521, - DatabaseFamily::Sqlite => 0, - } - } - - pub fn username(&self) -> &'static str { - match self { - DatabaseFamily::Oracle => "system", - _ => "haze", - } - } - - pub fn db(&self) -> &'static str { - match self { - DatabaseFamily::Oracle => "SYSTEM", - _ => "haze", - } - } } #[derive(Clone, Debug, Eq, PartialEq, Default)] diff --git a/src/exec.rs b/src/exec.rs index f206a32..f077329 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -75,7 +75,7 @@ pub async fn exec_tty, S2: Into, Env: Into>( } }); - // set stdout in raw mode so we can do TTY stuff + // set stdout in raw mode so we can do tty stuff let mut stdout = stdout.lock().into_raw_mode().into_diagnostic()?; // pipe docker exec output into stdout diff --git a/src/help.rs b/src/help.rs index fec9415..566bff7 100644 --- a/src/help.rs +++ b/src/help.rs @@ -151,14 +151,12 @@ fn subcommand_help(command: &dyn SubCommand) { for service in ServiceType::iter() { let service: ServiceType = service; let service_str: &'static str = service.into(); - if let Some(doc) = service.get_documentation() { - println!( - " {}{} {}", - service.blue(), - " ".repeat(max_service_len - service_str.len()), - doc, - ); - } + println!( + " {}{} {}", + service.blue(), + " ".repeat(max_service_len - service_str.len()), + service.get_documentation().unwrap_or_default(), + ); } println!(); diff --git a/src/image.rs b/src/image.rs index 26850d9..e24abfd 100644 --- a/src/image.rs +++ b/src/image.rs @@ -1,6 +1,6 @@ -use bollard::Docker; use bollard::models::CreateImageInfo; use bollard::query_parameters::CreateImageOptions; +use bollard::Docker; use futures_util::StreamExt; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use miette::{IntoDiagnostic, Result, WrapErr}; diff --git a/src/main.rs b/src/main.rs index 2271d5c..2c01ce4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -129,11 +129,7 @@ async fn main() -> Result { HazeArgs::List { filter } => { let list = Cloud::list(&docker, filter, &config).await?; for cloud in list { - let mut services: Vec<_> = cloud - .services() - .filter(|service| !service.hidden()) - .map(Service::name) - .collect(); + let mut services: Vec<_> = cloud.services().map(Service::name).collect(); services.push(cloud.db().name()); let services = services.join(", "); let pin = if cloud.is_pinned(&docker).await.unwrap_or(false) { @@ -286,26 +282,11 @@ async fn main() -> Result { .await?; } } - HazeArgs::Open { filter, service } => { + HazeArgs::Open { filter } => { let cloud = Cloud::get_by_filter(&docker, filter, &config).await?; - match service { - Some(service) => { - let Some(ip) = service.get_ips(&docker, &cloud.id).await?.next() else { - eprintln!( - "Service {} can't be opened as it has no associated IP", - service.name() - ); - return Ok(ExitCode::FAILURE); - }; - let addr = config - .proxy - .addr(&format!("{}-{}", cloud.id, service.name()), ip); - opener::open(addr).into_diagnostic()?; - } - None => match cloud.ip { - Some(_) => opener::open(cloud.address).into_diagnostic()?, - None => eprintln!("{} is not running", cloud.id), - }, + match cloud.ip { + Some(_) => opener::open(cloud.address).into_diagnostic()?, + None => eprintln!("{} is not running", cloud.id), } } HazeArgs::Test { options, mut args } => { diff --git a/src/mapping.rs b/src/mapping.rs index 30b82ef..6291c53 100644 --- a/src/mapping.rs +++ b/src/mapping.rs @@ -107,7 +107,7 @@ impl<'a> Mapping<'a> { .wrap_err("Failed to create source file")?, } - // For worktrees, pre-create mount points within /var/www/html to prevent docker from creating them as root. + // For worktrees, pre-create mountpoints within /var/www/html to prevent docker from creating them as root. if self.target.starts_with("/var/www/html") { let local_target = source_root.join( self.target diff --git a/src/network.rs b/src/network.rs index 1ea2050..386e13c 100644 --- a/src/network.rs +++ b/src/network.rs @@ -1,6 +1,6 @@ use crate::cloud::Cloud; -use bollard::Docker; use bollard::config::NetworkCreateRequest; +use bollard::Docker; use miette::{IntoDiagnostic, Result, WrapErr}; pub async fn clear_networks(docker: &Docker, instances: &[Cloud]) -> Result<()> { @@ -12,10 +12,9 @@ pub async fn clear_networks(docker: &Docker, instances: &[Cloud]) -> Result<()> for network in networks { if let Some(name) = network.name.as_deref() && let Some(id) = name.strip_prefix("haze-") - && !instances.iter().any(|cloud| cloud.id == id) - { - docker.remove_network(name).await.ok(); - } + && !instances.iter().any(|cloud| cloud.id == id) { + docker.remove_network(name).await.ok(); + } } Ok(()) } diff --git a/src/php.rs b/src/php.rs index e56267a..f0878a5 100644 --- a/src/php.rs +++ b/src/php.rs @@ -1,13 +1,13 @@ use crate::config::ProxyConfig; use crate::database::Database; -use crate::image::{ImageVersion, image_version, pull_image}; +use crate::image::{image_version, pull_image, ImageVersion}; use crate::network::ensure_network_exists; use crate::service::Service; use crate::service::ServiceTrait; -use bollard::Docker; use bollard::config::{ContainerCreateBody, NetworkConnectRequest, NetworkingConfig}; use bollard::models::{EndpointSettings, HostConfig}; use bollard::query_parameters::CreateContainerOptions; +use bollard::Docker; use itertools::Itertools; use maplit::hashmap; use miette::{IntoDiagnostic, Report, Result, WrapErr}; @@ -127,16 +127,15 @@ impl PhpVersion { let image_version = image_version(docker, self.image()).await; let haze_version = ImageVersion::from_str(env!("CARGO_PKG_VERSION")); if let (Some(image_version), Ok(haze_version)) = (image_version, haze_version) - && image_version < haze_version - { - eprintln!( - "{}: image version is out of date, run {} to update.", - "Warning".red(), - "haze update".blue() - ); - eprintln!(" Haze version: {}", haze_version.bright_yellow()); - eprintln!(" Image version: {}", image_version.bright_yellow()); - } + && image_version < haze_version { + eprintln!( + "{}: image version is out of date, run {} to update.", + "Warning".red(), + "haze update".blue() + ); + eprintln!(" Haze version: {}", haze_version.bright_yellow()); + eprintln!(" Image version: {}", image_version.bright_yellow()); + } let options = Some(CreateContainerOptions { name: Some(id.to_string()), diff --git a/src/proxy.rs b/src/proxy.rs index cc6f6ea..2223299 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,8 +1,8 @@ -use crate::Result; use crate::service::{ServiceTrait, ServiceType}; +use crate::Result; use crate::{Cloud, HazeConfig}; -use axum::http::HeaderValue; use axum::http::header::HOST; +use axum::http::HeaderValue; use axum::{ body::Body, extract::Request, @@ -10,13 +10,13 @@ use axum::{ }; use bollard::Docker; use futures_util::StreamExt; -use hyper::StatusCode; use hyper::body::Incoming; use hyper::server::conn::http1; use hyper::service::service_fn; +use hyper::StatusCode; use hyper_util::rt::TokioIo; use hyper_util::{client::legacy::connect::HttpConnector, rt::TokioExecutor}; -use miette::{IntoDiagnostic, miette}; +use miette::{miette, IntoDiagnostic}; use std::collections::HashMap; use std::convert::Infallible; use std::fs::{create_dir_all, set_permissions}; @@ -237,11 +237,10 @@ async fn serve( } else { let listen: PathBuf = listen.into(); if let Some(parent) = listen.parent() - && !parent.exists() - { - create_dir_all(parent).into_diagnostic()?; - set_permissions(parent, PermissionsExt::from_mode(0o755)).into_diagnostic()?; - } + && !parent.exists() { + create_dir_all(parent).into_diagnostic()?; + set_permissions(parent, PermissionsExt::from_mode(0o755)).into_diagnostic()?; + } let _ = tokio::fs::remove_file(&listen).await; let listener = UnixListener::bind(&listen).unwrap(); @@ -338,7 +337,7 @@ async fn handler(state: AppState, mut req: Request) -> Result shell, - (None, Some("nu")) => vec!["nu".into()], - _ => vec!["bash".into()], + let shell = if path.extension() == Some("nu") { + "nu" + } else { + "bash" }; options.mappings.push( @@ -63,10 +61,7 @@ pub async fn run_script( cloud .exec( docker, - shell - .into_iter() - .chain(once(target_path.into_string())) - .collect(), + vec![shell.to_string(), target_path.into_string()], true, get_forward_env(), ) @@ -79,22 +74,14 @@ pub async fn run_script( Ok(()) } -#[derive(Debug)] -struct Script { - options: CloudOptions, - mode: ScriptMode, - shell: Option>, -} - -fn parse_script(script: &str, config: &HazeConfig) -> Result