# Hwaro Fast and lightweight static site generator built with Crystal Base URL: https://hwaro.hahwul.com This is documentation for Hwaro, an open-source static site generator. Content is provided under the MIT license. --- Title: Hwaro Documentation URL: https://hwaro.hahwul.com/ Source: content/index.md Language: en Hwaro(화로) is a fast, lightweight static site generator built with Crystal. --- Title: Deploy URL: https://hwaro.hahwul.com/deploy/ Source: content/deploy/_index.md Language: en Deploy your Hwaro site to any static hosting provider. ## Build for Production ```bash hwaro build ``` This generates static files in `public/`. You can optionally minify output: ```bash hwaro build --minify ``` This performs conservative optimization: HTML comments and trailing whitespace are removed, and JSON/XML whitespace is compacted. All code blocks and content structure are preserved. Everything in `static/` is copied into `public/` and deployed, including hidden dot-paths such as `.well-known/security.txt` and `.domains`, identically for cold and `--cache`/incremental builds. Common OS/editor/VCS cruft (`.DS_Store`, `.git/`, …) is filtered out automatically; see [Static Files](/start/config/#static-files) to tune it. > **Never deploy `hwaro serve` output.** The dev server builds into its own `.hwaro/serve/` directory (leaving `public/` untouched) with its dev `base_url` (e.g. `http://127.0.0.1:3000`) baked into every link, and stamps that directory with a `.hwaro-dev` marker. `hwaro deploy` refuses any source directory carrying the marker; if you hit that error, run `hwaro build` and deploy the freshly built output instead. ## General Steps 1. Build the site: `hwaro build` 2. Upload `public/` directory to your host (or use `hwaro deploy`) 3. Configure your domain ## Built-in Deploy Command Hwaro includes `hwaro deploy` for deploying to configured targets: ```bash hwaro deploy # Deploy to the first configured target hwaro deploy s3 # Deploy to a specific target by name hwaro deploy s3 backup # Deploy to multiple targets hwaro deploy --dry-run # Preview changes ``` See [Deploy Configuration](/deploy/config/) for full target setup, matchers, and options. ## Platform Guides See the platform-specific guides below for step-by-step deployment instructions. --- Title: Cloudflare Pages URL: https://hwaro.hahwul.com/deploy/cloudflare-pages/ Source: content/deploy/cloudflare-pages/index.md Language: en Deploy your Hwaro site to Cloudflare Pages for fast global delivery. ## Quick Start ### Generate Config ```bash hwaro tool platform cloudflare ``` This creates a `wrangler.toml` with project settings and site bucket configuration. ### Deploy via Dashboard 1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com) > **Workers & Pages** 2. Click **Create application** > **Pages** > **Connect to Git** 3. Select your repository 4. Set build configuration: - **Build command**: `hwaro build` - **Build output directory**: `public` 5. Click **Save and Deploy** ### Deploy via Wrangler CLI ```bash # Install Wrangler npm install -g wrangler # Build the site hwaro build # Deploy wrangler pages deploy public --project-name my-site ``` ## Manual Configuration If you prefer to configure manually, set these in the Cloudflare Pages dashboard: | Setting | Value | |---------|-------| | Build command | `hwaro build` | | Build output directory | `public` | Or create `wrangler.toml`: ```toml name = "my-site" compatibility_date = "2024-01-01" # Use current date when deploying [site] bucket = "./public" ``` ## Redirects Cloudflare Pages uses a `_redirects` file in the output directory. Create `static/_redirects` with redirect rules for your page aliases: ``` /old-url/ /posts/new-post/ 301 /legacy/post/ /posts/new-post/ 301 ``` Aliases are defined in page frontmatter: ```markdown --- title: New Post aliases: - /old-url/ - /legacy/post/ --- ``` When using `hwaro tool platform cloudflare`, the generated `wrangler.toml` includes comments listing these redirects so you can copy them into `static/_redirects`. ## Custom Domain 1. Go to **Workers & Pages** > your project > **Custom domains** 2. Click **Set up a custom domain** 3. Follow DNS configuration instructions 4. Update `base_url` in `config.toml`: ```toml base_url = "https://www.yourdomain.com" ``` ## Preview Deployments Cloudflare Pages automatically creates preview deployments for every branch push. Preview URLs follow the pattern: `..pages.dev`. ## See Also - [Platform Config Generator](/start/tools/platform/) — Detailed generator options - [CLI Reference](/start/cli/#tool) — All tool commands - Other platforms: [Netlify](/deploy/netlify/) | [Vercel](/deploy/vercel/) | [GitHub Pages](/deploy/github-pages/) --- Title: Codeberg Pages URL: https://hwaro.hahwul.com/deploy/codeberg-pages/ Source: content/deploy/codeberg-pages/index.md Language: en Deploy your Hwaro site to [Codeberg Pages](https://codeberg.page/), free static hosting backed by Codeberg's Forgejo instance. ## How Codeberg Pages Works Codeberg serves static sites in two modes: - **User / org site** — a repository named **`pages`** under your account. Codeberg serves the *default branch* of that repo at `https://USERNAME.codeberg.page/`. - **Project site** — any other repository. Codeberg serves a branch named **`pages`** at `https://USERNAME.codeberg.page/REPO/`. This distinction matters: a project site pushes to a `pages` *branch*, while a user site pushes to the *default branch* (typically `main`) of the `pages` *repo*. The workflow below defaults to the project-site case and exposes `PAGES_BRANCH` so the user-site case is a one-line change. ## Prerequisites - Codeberg account - Repository on Codeberg (either `pages` for a user site, or any repo for a project site) - A Codeberg access token with `write:repository` scope (Settings → Applications → Generate new token) ## Method 1: Forgejo Actions (Recommended) Codeberg supports [Forgejo Actions](https://forgejo.org/docs/latest/user/actions/), which is GitHub Actions-compatible. Forgejo Actions is opt-in per repository, so enable it under **Settings → Actions** before the workflow will run. > Forgejo also accepts workflows under `.gitea/workflows/` for backwards compatibility, but `.forgejo/workflows/` is the upstream-blessed path and the one Hwaro generates. ### Generate the Workflow ```bash hwaro tool platform codeberg-pages ``` This writes `.forgejo/workflows/deploy.yml`: ```yaml --- name: Hwaro Deploy on: push: branches: [main] workflow_dispatch: jobs: deploy: runs-on: docker container: image: ghcr.io/hahwul/hwaro:latest env: # Project site: "pages" (default). User/org site (repo named # "pages"): override to your default branch, e.g. "main". PAGES_BRANCH: pages steps: - name: Checkout uses: actions/checkout@v4 - name: Build site run: hwaro build - name: Deploy to Codeberg Pages env: CODEBERG_TOKEN: ${{ secrets.CODEBERG_TOKEN }} run: | cd public git init -b "$PAGES_BRANCH" git config user.name "${{ github.actor }}" git config user.email "${{ github.actor }}@noreply.codeberg.org" git add -A git commit -m "Deploy: $(date -u +'%Y-%m-%dT%H:%M:%SZ')" git push --force \ "https://${{ github.actor }}:$CODEBERG_TOKEN@codeberg.org/${{ github.repository }}.git" \ "$PAGES_BRANCH" ``` > **Branch history is not preserved.** Each run starts with a fresh `git init` and force-pushes, so the published branch is treated as a publish-only artifact. Keep your source on `main` (or wherever you develop); never edit the `pages` branch by hand. ### User Site vs Project Site The default `PAGES_BRANCH: pages` targets a **project site** (any repo, served at `USERNAME.codeberg.page/REPO/`). For a **user / org site** in a repo literally named `pages`, change the branch to your default branch: ```yaml env: PAGES_BRANCH: main # default branch of the `pages` repo ``` ### Add the Token Secret 1. Go to your repository **Settings → Actions → Secrets** 2. Add a new secret named `CODEBERG_TOKEN` 3. Paste your Codeberg access token (with `write:repository` scope) ### Set Base URL Update `config.toml`: ```toml # User/org site (repo named "pages") base_url = "https://USERNAME.codeberg.page" # Project site (any other repo) base_url = "https://USERNAME.codeberg.page/REPO" ``` Push to `main` and the workflow will build and deploy automatically. ## Method 2: Deploy via `hwaro deploy` If you'd rather deploy from your local machine, wire it up as a `[[deployment.targets]]` entry that calls a small shell script. Create `scripts/deploy-codeberg.sh`: ```bash #!/bin/bash set -e SOURCE_DIR="${1:?Usage: deploy-codeberg.sh }" REMOTE_URL="${CODEBERG_REMOTE:-$(git remote get-url origin)}" PAGES_BRANCH="${PAGES_BRANCH:-pages}" TMPDIR=$(mktemp -d) cp -r "$SOURCE_DIR"/. "$TMPDIR" cd "$TMPDIR" git init -b "$PAGES_BRANCH" git add -A git commit -m "Deploy to Codeberg Pages" git push --force "$REMOTE_URL" "$PAGES_BRANCH" rm -rf "$TMPDIR" ``` ```bash chmod +x scripts/deploy-codeberg.sh ``` Add the target to `config.toml`: ```toml [[deployment.targets]] name = "codeberg-pages" command = "scripts/deploy-codeberg.sh {source}" ``` Then build and deploy: ```bash hwaro build hwaro deploy codeberg-pages # Preview without deploying hwaro deploy codeberg-pages --dry-run # User site (push to the default branch instead of `pages`) PAGES_BRANCH=main hwaro deploy codeberg-pages ``` As with Method 1, the script force-pushes a fresh init, so branch history is not preserved. ## Method 3: Manual Branch Deploy ```bash hwaro build cd public git init -b pages git add -A git commit -m "Deploy" git push --force https://codeberg.org/USERNAME/REPO.git pages ``` For a user site (`pages` repo), substitute the default branch name (e.g. `main`) for `pages` in both the `init` and `push` commands. ## Custom Domain Codeberg Pages supports custom domains via a `.domains` file plus a DNS record. See the [official Codeberg docs](https://docs.codeberg.org/codeberg-pages/using-custom-domain/) for the authoritative version. > **Repo names with dots are unsupported.** Use `-` or `_` in the repo name if you plan to attach a custom domain. ### 1. Add a `.domains` file Place a `.domains` file at the root of the served branch listing the domains, one per line. The **first line is the canonical domain**; all subsequent domains are 301-redirected to it. Empty lines and `#` comments are allowed. ``` www.example.org example.org ``` Drop it under `static/.domains` so Hwaro copies it into `public/` on every build: ``` static/ └── .domains ``` Hwaro copies hidden dot-paths from `static/` verbatim into `public/` on every build (cold or `--cache`/incremental), so `static/.domains` always reaches the served branch. See [Static Files](/start/config/#static-files) for details. ### 2. Configure DNS Pick **one** of the three options below. **Option A — CNAME (recommended, simplest).** Point your domain at one of these names: | Site type | CNAME target | |---------------|-------------------------------------------| | Personal site | `USERNAME.codeberg.page` | | Project site | `REPO.USERNAME.codeberg.page` | | Custom branch | `BRANCH.REPO.USERNAME.codeberg.page` | CNAME delegates *the whole hostname*, so you cannot run email (MX) on the same name with this option. **Option B — ALIAS + TXT.** If your DNS provider supports `ALIAS` (or `ANAME`) records: | Type | Name | Value | |-------|------|--------------------------------------| | ALIAS | @ | `codeberg.page` | | TXT | @ | `REPO.USERNAME.codeberg.page` (or `USERNAME.codeberg.page` for a user site) | **Option C — A + AAAA + TXT.** Use this if your provider doesn't support ALIAS, or if your zone uses DNSSEC (which is incompatible with the `codeberg.page` CNAME): | Type | Name | Value | |------|------|--------------------------------------| | A | @ | `217.197.84.141` | | AAAA | @ | `2a0a:4580:103f:c0de::2` | | TXT | @ | `REPO.USERNAME.codeberg.page` (or `USERNAME.codeberg.page` for a user site) | > Verify the latest IPs on the [Codeberg Pages docs](https://docs.codeberg.org/codeberg-pages/using-custom-domain/) before relying on them. Codeberg occasionally rotates them. If your zone uses CAA records, add an entry that allows Let's Encrypt so Codeberg can issue a TLS certificate: ``` @ CAA 0 issue "letsencrypt.org" ``` ### 3. Update `base_url` ```toml base_url = "https://www.example.org" ``` ## Troubleshooting ### Workflow Doesn't Run - Make sure Forgejo Actions is enabled under **Settings → Actions** for the repository - Confirm the workflow file is committed at `.forgejo/workflows/deploy.yml` (or `.gitea/workflows/deploy.yml`) ### Push Fails with 401 / 403 - Re-check the `CODEBERG_TOKEN` secret value and that the token still has `write:repository` scope - Tokens are tied to your account, so make sure the actor has push access to the target repo ### 404 on the Deployed Site - Codeberg Pages may take a minute or two to publish after the first push - For a project site, confirm the branch is named exactly `pages` - For a user site, confirm the repo is named exactly `pages` and `PAGES_BRANCH` matches its default branch - Verify `base_url` matches the published URL (no trailing slash) ### Custom Domain Doesn't Resolve to HTTPS - Confirm the `.domains` file is present at the root of the published branch - If you have CAA records, make sure `letsencrypt.org` is allowed - Allow a few minutes for the certificate to be issued after DNS propagates ## See Also - [Deploy Configuration](/deploy/config/) — Target setup and matchers - [CLI Reference](/start/cli/) — All deploy command options - [Codeberg Pages docs](https://docs.codeberg.org/codeberg-pages/) — Upstream reference - Other platforms: [GitHub Pages](/deploy/github-pages/) | [GitLab CI](/deploy/gitlab-ci/) | [Netlify](/deploy/netlify/) | [Cloudflare Pages](/deploy/cloudflare-pages/) --- Title: Deploy Configuration URL: https://hwaro.hahwul.com/deploy/config/ Source: content/deploy/config.md Language: en Configure deployment targets for the `hwaro deploy` command in `config.toml`. ## Global Options ```toml [deployment] target = "prod" source_dir = "public" confirm = false dry_run = false max_deletes = 256 ``` | Key | Type | Default | Description | |-----|------|---------|-------------| | target | string | — | Default target name to deploy to | | source_dir | string | "public" | Directory containing the built site | | confirm | bool | false | Prompt for confirmation before deploying | | dry_run | bool | false | Show what would be deployed without making changes | | force | bool | false | Force deployment even if no changes detected | | max_deletes | int | 256 | Safety limit on file deletions (any negative value disables the limit) | `max_deletes` bounds the **built-in** `file://` sync only. Command targets (`s3://`, `gs://`, `az://`, or an explicit `command`) delete through the external tool's own flags, which hwaro cannot count in advance. A deploy also refuses outright when the source directory is empty, or when `include`/`exclude` selected no files while the destination still holds some. That combination is almost always "the site was never built" and would otherwise wipe the destination. Pass `--force` to clear a destination on purpose. A `command` target that never interpolates `{source}` is exempt. A source directory containing a `.hwaro-dev` marker is refused as well. That marker means the directory is `hwaro serve` output, with the dev server's `base_url` (e.g. `http://127.0.0.1:3000`) baked into every link. Run `hwaro build` and deploy its output instead; there is no override flag (deleting the marker file by hand is the deliberate escape hatch). `workers` is accepted for forward compatibility but not applied: the built-in sync copies serially and command targets manage their own concurrency. Setting it prints a warning. ## Targets Define one or more deployment targets: ```toml [[deployment.targets]] name = "prod" url = "file:///var/www/mysite" [[deployment.targets]] name = "s3" url = "s3://my-bucket" # Auto-generates: aws s3 sync {source}/ s3://my-bucket --delete [[deployment.targets]] name = "custom" url = "s3://my-bucket" command = "aws s3 sync {source}/ {url} --delete --exclude '.git/*'" # Custom command overrides auto-generation ``` **Auto-generated commands by URL scheme:** | Scheme | Command | Requires | |--------|---------|----------| | `file://` | Built-in directory sync | — | | `s3://` | `aws s3 sync {source}/ {url} --delete` | AWS CLI | | `gs://` | `gsutil -m rsync -r -d {source}/ {url}` | Google Cloud SDK | | `az://` | `az storage blob sync --source {source} --container [--destination ]` | Azure CLI | For `az://container/sub/dir` URLs the path becomes the `--destination` prefix inside the container. If a `command` field is set, it always takes priority over auto-generation. A value that starts with a URL scheme is never treated as a local path, so a single-slash typo (`s3:/bucket`) fails with an unsupported-scheme error instead of quietly creating a directory named `s3:`. `include`, `exclude`, and `strip_index_html` apply to the built-in `file://` sync only; on command targets they warn, because the external tool receives the whole source tree. **Local directory sync and symlinks.** The built-in sync keeps every write inside the destination. A symlink standing where a file or directory belongs is replaced with the real thing, and a symlink with no counterpart in the source is unlinked. Neither case reads or deletes through the link, so content living outside the destination is never touched. | Key | Type | Default | Description | |-----|------|---------|-------------| | name | string | — | Target identifier (must be unique; duplicates are warned about and only the first is used) | | url | string | — | Destination URL (`file://`, `s3://`, `gs://`, `az://`) | | path | string | — | Alias for `url` when deploying to a local directory (`path = "~/public"`; `~` is expanded) | | include | string | — | Glob pattern for files to include | | exclude | string | — | Glob pattern for files to exclude | | strip_index_html | bool | false | Remove `index.html` from URLs | | command | string | — | Custom command (overrides auto-generation) | Custom commands support placeholders: | Placeholder | Description | |-------------|-------------| | `{source}` | Source directory (default: `public`) | | `{url}` | Target URL | | `{target}` | Target name | ## Matchers Configure per-file deployment settings using pattern matchers: ```toml [[deployment.matchers]] pattern = "^.+\\.html$" force = true ``` | Key | Type | Default | Description | |-----|------|---------|-------------| | pattern | string | — | Regex pattern to match file paths | | force | bool | false | Always copy matched files, even when identical at the destination | | cache_control | string | — | Reserved — not applied by the built-in sync (see below) | | content_type | string | — | Reserved — not applied by the built-in sync (see below) | | gzip | bool | false | Reserved — not applied by the built-in sync (see below) | The built-in sync copies files and runs external CLIs; it does not talk to an object-store API, so it can only honor `force`. Setting `cache_control`, `content_type`, or `gzip` prints a warning. Configure headers and compression at your host or CDN instead. ## See Also - [CLI Reference](/start/cli/) — All deploy command-line options - [Features: Deployment](/features/deployment/) — Quick overview --- Title: Docker URL: https://hwaro.hahwul.com/deploy/docker/ Source: content/deploy/docker/index.md Language: en Deploy your Hwaro site using Docker. ## Official Image The official Docker image is available at `ghcr.io/hahwul/hwaro`. ```bash docker pull ghcr.io/hahwul/hwaro:latest ``` ## Multi-stage Build You can use a multi-stage build to compile your site and serve it with a lightweight web server like Nginx. Create a `Dockerfile` in your project root: ```dockerfile # Stage 1: Build the site FROM ghcr.io/hahwul/hwaro:latest AS builder WORKDIR /site COPY . . # Build the site RUN hwaro build # Stage 2: Serve with Nginx FROM nginx:alpine # Copy built assets from builder stage COPY --from=builder /site/public /usr/share/nginx/html # Expose port 80 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] ``` ### Build and Run ```bash # Build the image docker build -t my-hwaro-site . # Run the container docker run -d -p 8080:80 my-hwaro-site ``` Visit `http://localhost:8080` to see your site. ## CLI Usage You can run Hwaro commands directly using the Docker image without installing Crystal or Hwaro locally. ```bash # Build the site docker run --rm -v $(pwd):/site -w /site ghcr.io/hahwul/hwaro build # Interactive shell docker run --rm -it -v $(pwd):/site -w /site ghcr.io/hahwul/hwaro /bin/sh ``` ## See Also - [Deploy Configuration](/deploy/config/) — Target setup and matchers - [CLI Reference](/start/cli/) — All build/deploy options - Other platforms: [GitHub Pages](/deploy/github-pages/) | [Netlify](/deploy/netlify/) | [Vercel](/deploy/vercel/) --- Title: GitHub Pages URL: https://hwaro.hahwul.com/deploy/github-pages/ Source: content/deploy/github-pages/index.md Language: en Deploy your Hwaro site to GitHub Pages for free hosting. ## Prerequisites - GitHub repository - Hwaro site ready to build ## Method 1: GitHub Actions (Recommended) Use the official [`hahwul/hwaro`](https://github.com/hahwul/hwaro) action to build and deploy without installing Hwaro manually. ### Create Workflow You can auto-generate the workflow file using: ```bash hwaro tool platform github-pages ``` Or create `.github/workflows/deploy.yml` manually: ```yaml --- name: Hwaro CI/CD on: push: branches: [main] pull_request: branches: [main] workflow_dispatch: permissions: contents: write jobs: build: runs-on: ubuntu-latest if: github.event_name == 'pull_request' steps: - name: Checkout uses: actions/checkout@v6 - name: Build Only uses: hahwul/hwaro@main with: build_only: true deploy: runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - name: Checkout uses: actions/checkout@v6 - name: Build and Deploy uses: hahwul/hwaro@main with: token: ${{ secrets.GITHUB_TOKEN }} ``` ### Action Inputs | Input | Description | Default | |-------|-------------|---------| | `build_dir` | Directory containing the Hwaro site | `.` (repository root) | | `build_only` | Only build without deploying | `false` | | `token` | GitHub token for deployment | — | If your Hwaro site is in a subdirectory (e.g., `docs/`), set `build_dir`: ```yaml - name: Build and Deploy uses: hahwul/hwaro@main with: build_dir: "docs" token: ${{ secrets.GITHUB_TOKEN }} ``` ### OG Image Caching The action automatically caches OG images between deploys. Before each build, it restores previously generated images from the `gh-pages` branch and enables `--cache` mode. Only pages with changed content (title, description, URL) or updated OG config will have their images regenerated. This significantly speeds up builds for large sites. ### Configure GitHub Pages 1. Go to repository **Settings** → **Pages** 2. Under "Build and deployment", select **Deploy from a branch** 3. Choose `gh-pages` branch, `/ (root)` folder 4. Push to `main` branch to trigger deployment ### Set Base URL Update `config.toml`: ```toml # For user/org site (username.github.io) base_url = "https://username.github.io" # For project site (username.github.io/repo) base_url = "https://username.github.io/repo" ``` ## Method 2: Deploy via `hwaro deploy` Create a deploy script `scripts/deploy-ghpages.sh`: ```bash #!/bin/bash set -e SOURCE_DIR="${1:?Usage: deploy-ghpages.sh }" REMOTE_URL=$(git remote get-url origin) TMPDIR=$(mktemp -d) cp -r "$SOURCE_DIR"/. "$TMPDIR" touch "$TMPDIR/.nojekyll" cd "$TMPDIR" git init -b gh-pages git add -A git commit -m "Deploy to GitHub Pages" git push --force "$REMOTE_URL" gh-pages rm -rf "$TMPDIR" ``` ```bash chmod +x scripts/deploy-ghpages.sh ``` Add the target to `config.toml`: ```toml [[deployment.targets]] name = "github-pages" command = "scripts/deploy-ghpages.sh {source}" ``` Then build and deploy: ```bash hwaro build hwaro deploy github-pages # Preview without deploying hwaro deploy github-pages --dry-run ``` ### Configure GitHub Pages 1. Go to repository **Settings** → **Pages** 2. Under "Build and deployment", select **Deploy from a branch** 3. Choose `gh-pages` branch, `/ (root)` folder 4. Click **Save** ## Method 3: Manual Branch Deploy ```bash hwaro build # Create orphan branch git checkout --orphan gh-pages # Remove all files git rm -rf . # Copy built site cp -r public/* . # Commit and push git add . git commit -m "Deploy site" git push origin gh-pages --force # Return to main branch git checkout main ``` ## Custom Domain ### Configure DNS Add a CNAME record pointing to `username.github.io`: | Type | Name | Value | |------|------|-------| | CNAME | www | username.github.io | | A | @ | 185.199.108.153 | | A | @ | 185.199.109.153 | | A | @ | 185.199.110.153 | | A | @ | 185.199.111.153 | ### Add CNAME File Create `static/CNAME`: ``` www.yourdomain.com ``` ### Update Config ```toml base_url = "https://www.yourdomain.com" ``` ### Enable HTTPS 1. Go to repository **Settings** → **Pages** 2. Check **Enforce HTTPS** ## Troubleshooting ### 404 Errors - Check `base_url` matches your GitHub Pages URL - Ensure CNAME file is in `static/` directory - Wait a few minutes for deployment to complete ### Assets Not Loading - Verify `base_url` has no trailing slash - Check asset paths use `{{ base_url }}` prefix ### Build Failures - Review Actions logs for error messages - Check that `build_dir` points to the correct directory ## Example Repository Structure ``` my-site/ ├── .github/ │ └── workflows/ │ └── deploy.yml ├── content/ ├── templates/ ├── static/ │ └── CNAME ├── config.toml └── README.md ``` ## See Also - [Deploy Configuration](/deploy/config/) — Target setup and matchers - [CLI Reference](/start/cli/) — All deploy command options - Other platforms: [GitLab CI](/deploy/gitlab-ci/) | [Netlify](/deploy/netlify/) | [Vercel](/deploy/vercel/) | [Codeberg Pages](/deploy/codeberg-pages/) --- Title: GitLab CI URL: https://hwaro.hahwul.com/deploy/gitlab-ci/ Source: content/deploy/gitlab-ci/index.md Language: en Deploy your Hwaro site using GitLab CI/CD. ## Quick Start Auto-generate the config file: ```bash hwaro tool platform gitlab-ci ``` ## Configuration Or add `.gitlab-ci.yml` to your repository manually: ```yaml image: ghcr.io/hahwul/hwaro:latest pages: script: - hwaro build artifacts: paths: - public only: - main ``` This configuration uses the official Hwaro Docker image to build your site and deploys the `public` directory to GitLab Pages. ## See Also - [Deploy Configuration](/deploy/config/) — Target setup and matchers - [CLI Reference](/start/cli/) — All deploy command options - Other platforms: [GitHub Pages](/deploy/github-pages/) | [Netlify](/deploy/netlify/) | [Vercel](/deploy/vercel/) --- Title: Netlify URL: https://hwaro.hahwul.com/deploy/netlify/ Source: content/deploy/netlify/index.md Language: en Deploy your Hwaro site to Netlify with automatic builds and global CDN. ## Quick Start ### Generate Config ```bash hwaro tool platform netlify ``` This creates a `netlify.toml` with build settings, redirects from aliases, and cache headers. ### Deploy via Git 1. Push your repository to GitHub, GitLab, or Bitbucket 2. Go to [Netlify](https://app.netlify.com) and click **Add new site** > **Import an existing project** 3. Connect your repository 4. Netlify will auto-detect `netlify.toml` settings 5. Click **Deploy site** ## Manual Configuration If you prefer to configure manually instead of using the generator, create `netlify.toml`: ```toml [build] command = "hwaro build" publish = "public" [build.environment] # Set environment variables as needed ``` ## Redirects Page aliases defined in frontmatter are automatically included as 301 redirects when using `hwaro tool platform netlify`: ```markdown --- title: New Post aliases: - /old-url/ - /legacy/post/ --- ``` Generates: ```toml [[redirects]] from = "/old-url/" to = "/posts/new-post/" status = 301 [[redirects]] from = "/legacy/post/" to = "/posts/new-post/" status = 301 ``` ## Custom Domain 1. Go to **Site settings** > **Domain management** 2. Click **Add custom domain** 3. Follow the DNS configuration instructions 4. Update `base_url` in `config.toml`: ```toml base_url = "https://www.yourdomain.com" ``` ## Deploy Previews Netlify automatically creates deploy previews for pull requests. Preview builds use a temporary URL, so override the base URL using a deploy context: ```toml [context.deploy-preview] command = "hwaro build --base-url $DEPLOY_PRIME_URL" ``` `$DEPLOY_PRIME_URL` is a Netlify-provided environment variable containing the unique preview URL (e.g., `https://deploy-preview-42--your-site.netlify.app`). ## See Also - [Platform Config Generator](/start/tools/platform/) — Detailed generator options - [CLI Reference](/start/cli/#tool) — All tool commands - Other platforms: [Vercel](/deploy/vercel/) | [Cloudflare Pages](/deploy/cloudflare-pages/) | [GitHub Pages](/deploy/github-pages/) --- Title: Vercel URL: https://hwaro.hahwul.com/deploy/vercel/ Source: content/deploy/vercel/index.md Language: en Deploy your Hwaro site to Vercel with zero-config deployments. ## Quick Start ### Generate Config ```bash hwaro tool platform vercel ``` This creates a `vercel.json` with build settings, redirects from aliases, and cache headers. ### Deploy via Git 1. Push your repository to GitHub, GitLab, or Bitbucket 2. Go to [Vercel](https://vercel.com) and click **Add New** > **Project** 3. Import your repository 4. Vercel will auto-detect `vercel.json` settings 5. Click **Deploy** ## Manual Configuration If you prefer to configure manually, create `vercel.json`: ```json { "buildCommand": "hwaro build", "outputDirectory": "public" } ``` ## Redirects Page aliases defined in frontmatter are automatically included as 301 redirects when using `hwaro tool platform vercel`: ```markdown --- title: New Post aliases: - /old-url/ --- ``` Generates: ```json { "redirects": [ { "source": "/old-url/", "destination": "/posts/new-post/", "statusCode": 301 } ] } ``` ## Custom Domain 1. Go to **Settings** > **Domains** 2. Add your custom domain 3. Follow DNS configuration instructions 4. Update `base_url` in `config.toml`: ```toml base_url = "https://www.yourdomain.com" ``` ## Preview Deployments Vercel automatically creates preview deployments for every push to non-production branches. No additional configuration is needed. ## See Also - [Platform Config Generator](/start/tools/platform/) — Detailed generator options - [CLI Reference](/start/cli/#tool) — All tool commands - Other platforms: [Netlify](/deploy/netlify/) | [Cloudflare Pages](/deploy/cloudflare-pages/) | [GitHub Pages](/deploy/github-pages/) --- Title: Features URL: https://hwaro.hahwul.com/features/ Source: content/features/_index.md Language: en Hwaro ships with a broad set of built-in features, and no plugins are required. All features are configured through `config.toml` and work out of the box once enabled. ## At a Glance **SEO & Discovery** — Sitemap, RSS/Atom feeds, robots.txt, OpenGraph, Twitter Cards, structured data (JSON-LD), and auto-generated OG images. **Content** — Syntax highlighting, search index generation, pagination, markdown extensions (footnotes, task lists, math, mermaid), series, related posts, and named navigation menus. **Performance** — Incremental builds, streaming builds for large sites, asset pipeline with minification and fingerprinting, image processing with responsive variants and LQIP placeholders, and cache busting. **Files & Environment** — Environment variables, environment-specific config overrides, auto-includes for CSS/JS, local and remote data, and content file publishing. **Platform** — Multilingual support, versioned docs, image processing, PWA, AMP, and built-in deployment to multiple targets. --- Title: AMP URL: https://hwaro.hahwul.com/features/amp/ Source: content/features/amp.md Language: en Hwaro can automatically generate AMP (Accelerated Mobile Pages) versions of your content alongside the regular pages. ## How It Works 1. Regular pages are rendered normally 2. After rendering, Hwaro reads each page's HTML and creates an AMP-compliant version 3. AMP pages are written under a configurable path prefix (default: `/amp/`) 4. A `` tag is injected into the canonical page's `` ## Configuration ```toml [amp] enabled = true path_prefix = "amp" sections = ["posts"] ``` | Key | Type | Default | Description | |-----|------|---------|-------------| | enabled | bool | false | Enable AMP page generation | | path_prefix | string | "amp" | URL prefix for AMP pages | | sections | array | [] | Sections to generate AMP for (empty = all) | ## What Gets Converted The AMP converter automatically applies these transformations: | Original | AMP Version | |----------|-------------| | `` | `` | | `` | `` | | `