feat: add ci

This commit is contained in:
Alexander Medvedev 2026-09-08 18:15:10 +02:00
parent a100ba9549
commit b1b2fe2e4a
11 changed files with 742 additions and 0 deletions

17
.dockerignore Normal file
View File

@ -0,0 +1,17 @@
# A whitelist of files that should be included into Docker.
# Put an exclamation mark before everything to include.
# Ignore everything
*
# Allow the source code folders
!/src/
!/wit/
!/vine.toml
# Allow build.rs or git sha
!/.git
# Dependencies
!Cargo.lock
!Cargo.toml

7
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,7 @@
<!-- Follow the Conventional Commits spec: <https://www.conventionalcommits.org/en/v1.0.0/> -->
## Description
## Testing
Please follow our [Coding Guidelines](https://github.com/Pumpkin-MC/Pumpkin/blob/master/CONTRIBUTING.md#coding-guidelines)

15
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,15 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for more information:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: weekly
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: weekly

150
.github/workflows/docker.yml vendored Normal file
View File

@ -0,0 +1,150 @@
# https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners
name: deploy-docker-image
env:
REGISTRY: ghcr.io
GITHUB_REPOSITORY: ${{ github.repository }}
on:
push:
branches:
- "master"
workflow_dispatch:
jobs:
docker-build-deploy:
if: github.repository == 'Pumpkin-MC/Vine'
permissions:
contents: read
packages: write
attestations: write
id-token: write
strategy:
matrix:
include:
- os: ubuntu-26.04-arm
platform: linux/arm64
- os: ubuntu-latest
platform: linux/amd64
runs-on: ${{matrix.os}}
steps:
- name: Set lowercase repository name
run: |
echo "REGISTRY_IMAGE=${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV}
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Checkout
uses: actions/checkout@v7
with:
submodules: recursive
- name: Login to GitHub CR
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Metadata
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}
- name: Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Build and Push
id: build
uses: docker/build-push-action@v7
with:
context: .
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v7
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
merge:
if: github.repository == 'Pumpkin-MC/Vine'
permissions:
contents: read
packages: write
attestations: write
id-token: write
runs-on: ubuntu-latest
needs:
- docker-build-deploy
steps:
- name: Set lowercase repository name
run: |
echo "REGISTRY_IMAGE=${GITHUB_REPOSITORY,,}" >> ${GITHUB_ENV}
- name: Checkout
uses: actions/checkout@v7
with:
submodules: recursive
- name: Download Digests
uses: actions/download-artifact@v8
with:
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true
- name: Extract Versions
id: versions
run: |
VERSION=$(awk -F '"' '/^version/ {print $2}' Cargo.toml)
echo "VINE_VERSION=$VERSION" >> $GITHUB_ENV
- name: Login to GitHub CR
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Extract Metadata
id: meta
uses: docker/metadata-action@v6
with:
images: ${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}
tags: |
type=ref,event=branch
type=raw,value=latest
type=sha
type=raw,value=${{ env.VINE_VERSION }}
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}

165
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,165 @@
name: Release
on:
push:
tags:
- "*"
- "!nightly"
- "!nightly*"
workflow_dispatch:
inputs:
tag_name:
description: "Release Tag name (optional, defaults to Cargo.toml version)"
required: false
type: string
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-Dwarnings"
jobs:
build_release:
name: Build & Sign Release
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
[
ubuntu-latest,
ubuntu-26.04-arm,
windows-latest,
windows-11-arm,
macos-latest,
]
toolchain:
- stable
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Checkout Pumpkin dependency
shell: bash
run: git clone --depth 1 https://github.com/Pumpkin-MC/Pumpkin.git ../Pumpkin
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- uses: Swatinem/rust-cache@v2
- name: Install musl-tools (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y musl-tools
- name: Build Release
shell: bash
run: |
cargo build --verbose --release
if [ "${{ runner.os }}" == "Linux" ]; then
if [ "${{ runner.arch }}" == "X64" ]; then
TARGET="x86_64-unknown-linux-musl"
elif [ "${{ runner.arch }}" == "ARM64" ]; then
TARGET="aarch64-unknown-linux-musl"
fi
rustup target add "$TARGET"
cargo build --verbose --release --target "$TARGET"
fi
- name: Prepare artifacts
shell: bash
run: |
mkdir -p dist
EXT=""
if [ "${{ runner.os }}" == "Windows" ]; then EXT=".exe"; fi
cp "target/release/vine$EXT" "dist/vine-${{ runner.arch }}-${{ runner.os }}$EXT"
if [ "${{ runner.os }}" == "Linux" ]; then
if [ "${{ runner.arch }}" == "X64" ]; then
TARGET="x86_64-unknown-linux-musl"
elif [ "${{ runner.arch }}" == "ARM64" ]; then
TARGET="aarch64-unknown-linux-musl"
fi
cp "target/$TARGET/release/vine" "dist/vine-${{ runner.arch }}-${{ runner.os }}-musl"
fi
- name: Upload unsigned executable for SignPath (Windows only)
if: runner.os == 'Windows' && env.SIGNPATH_API_TOKEN != ''
id: upload_unsigned
uses: actions/upload-artifact@v7
with:
name: unsigned-${{ runner.arch }}-${{ runner.os }}
path: dist/vine-*
retention-days: 1
- name: Submit SignPath signing request (Windows only)
if: runner.os == 'Windows' && env.SIGNPATH_API_TOKEN != ''
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: '${{ secrets.SIGNPATH_ORGANIZATION_ID }}'
project-slug: '${{ secrets.SIGNPATH_PROJECT_SLUG }}'
signing-policy-slug: '${{ secrets.SIGNPATH_RELEASE_SIGNING_POLICY_SLUG || secrets.SIGNPATH_SIGNING_POLICY_SLUG }}'
github-artifact-id: '${{ steps.upload_unsigned.outputs.artifact-id }}'
wait-for-completion: true
output-artifact-directory: dist/signed/
- name: Overwrite with signed version (Windows only)
if: runner.os == 'Windows' && env.SIGNPATH_API_TOKEN != ''
shell: bash
run: |
cp dist/signed/* dist/
rm -rf dist/signed/
- name: Export executable
uses: actions/upload-artifact@v7
with:
name: vine-${{ runner.arch }}-${{ runner.os }}
compression-level: 9
path: dist/vine-*
publish_release:
name: Publish GitHub Release
permissions:
contents: write
needs: [build_release]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Determine tag name
id: tag
shell: bash
run: |
if [ -n "${{ github.event.inputs.tag_name }}" ]; then
TAG="${{ github.event.inputs.tag_name }}"
elif [ "${{ github.ref_type }}" == "tag" ]; then
TAG="${{ github.ref_name }}"
else
VERSION=$(awk -F '"' '/^version/ {print $2}' Cargo.toml)
TAG="${VERSION}"
fi
echo "tag_name=${TAG}" >> $GITHUB_OUTPUT
echo "Determined release tag: ${TAG}"
- name: Download prebuilt binary artifacts
uses: actions/download-artifact@v8
with:
pattern: "vine-*"
path: dist/
merge-multiple: true
- name: Generate Checksums
shell: bash
run: |
cd dist
sha256sum vine-* > checksums.sha256
cat checksums.sha256
- name: Create GitHub Release
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ steps.tag.outputs.tag_name }}
name: Release ${{ steps.tag.outputs.tag_name }}
files: |
dist/vine*
dist/checksums.sha256
generate_release_notes: true

229
.github/workflows/rust.yml vendored Normal file
View File

@ -0,0 +1,229 @@
name: Cargo Build, Test, and Linting
on:
push:
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: "-Dwarnings"
jobs:
format:
name: Check formatting
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Checkout Pumpkin dependency
shell: bash
run: git clone --depth 1 https://github.com/Pumpkin-MC/Pumpkin.git ../Pumpkin
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: rustup component add rustfmt
- uses: Swatinem/rust-cache@v2
- run: cargo fmt --check
machete:
name: Detect unused dependencies
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Checkout Pumpkin dependency
shell: bash
run: git clone --depth 1 https://github.com/Pumpkin-MC/Pumpkin.git ../Pumpkin
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- uses: bnjbvr/cargo-machete@main
clippy_debug:
name: Run lints (debug)
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Checkout Pumpkin dependency
shell: bash
run: git clone --depth 1 https://github.com/Pumpkin-MC/Pumpkin.git ../Pumpkin
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: rustup component add clippy
- uses: Swatinem/rust-cache@v2
- name: Clippy (debug)
run: cargo clippy --all-targets --all-features
clippy_release:
name: Run lints (release)
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Checkout Pumpkin dependency
shell: bash
run: git clone --depth 1 https://github.com/Pumpkin-MC/Pumpkin.git ../Pumpkin
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: rustup component add clippy
- uses: Swatinem/rust-cache@v2
- name: Clippy (release)
run: cargo clippy --release --all-targets --all-features
build_and_test:
name: Build project and test
runs-on: ${{ matrix.os }}
needs: [format, machete, clippy_debug, clippy_release]
strategy:
matrix:
os:
[
ubuntu-latest,
ubuntu-26.04-arm,
windows-latest,
windows-11-arm,
macos-latest,
]
toolchain:
- stable
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Checkout Pumpkin dependency
shell: bash
run: git clone --depth 1 https://github.com/Pumpkin-MC/Pumpkin.git ../Pumpkin
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- uses: taiki-e/install-action@nextest
- uses: Swatinem/rust-cache@v2
- name: Cargo nextest
run: cargo nextest run --verbose
- name: Cargo doctest
run: cargo test --doc --verbose
build_release:
name: Build project in release
runs-on: ${{ matrix.os }}
needs: [build_and_test]
strategy:
matrix:
os:
[
ubuntu-latest,
ubuntu-26.04-arm,
windows-latest,
windows-11-arm,
macos-latest,
]
toolchain:
- stable
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Checkout Pumpkin dependency
shell: bash
run: git clone --depth 1 https://github.com/Pumpkin-MC/Pumpkin.git ../Pumpkin
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- uses: Swatinem/rust-cache@v2
- name: Install musl-tools (Linux only)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y musl-tools
- name: Build Release
shell: bash
run: |
cargo build --verbose --release
if [ "${{ runner.os }}" == "Linux" ]; then
if [ "${{ runner.arch }}" == "X64" ]; then
TARGET="x86_64-unknown-linux-musl"
elif [ "${{ runner.arch }}" == "ARM64" ]; then
TARGET="aarch64-unknown-linux-musl"
fi
rustup target add "$TARGET"
cargo build --verbose --release --target "$TARGET"
fi
- name: Prepare artifacts
shell: bash
run: |
mkdir -p dist
EXT=""
if [ "${{ runner.os }}" == "Windows" ]; then EXT=".exe"; fi
cp "target/release/vine$EXT" "dist/vine-${{ runner.arch }}-${{ runner.os }}$EXT"
if [ "${{ runner.os }}" == "Linux" ]; then
if [ "${{ runner.arch }}" == "X64" ]; then
TARGET="x86_64-unknown-linux-musl"
elif [ "${{ runner.arch }}" == "ARM64" ]; then
TARGET="aarch64-unknown-linux-musl"
fi
cp "target/$TARGET/release/vine" "dist/vine-${{ runner.arch }}-${{ runner.os }}-musl"
fi
- name: Export executable
uses: actions/upload-artifact@v7
with:
name: vine-${{ runner.arch }}-${{ runner.os }}
compression-level: 9
path: dist/vine-*
draft_release:
permissions:
contents: write
needs: [build_release]
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Download prebuilt binary artifacts
uses: actions/download-artifact@v8
with:
pattern: "**/vine-*"
path: dist/
merge-multiple: true
- name: Generate release notes
run: |
tree dist/ || ls -la dist/
echo "From commit: $(git rev-parse --short HEAD)" > dist/RELEASE.md
echo "Generated on: $(date -u +"%Y-%m-%d %H:%M") UTC" >> dist/RELEASE.md
cat dist/RELEASE.md
- name: Update the nightly tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag --force nightly && git push --force origin tag nightly
- name: Draft a nightly github release
uses: softprops/action-gh-release@v3
with:
prerelease: true
files: dist/vine*
tag_name: nightly
name: Nightly Build
body_path: dist/RELEASE.md

24
.github/workflows/typos.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Spelling
permissions:
contents: read
on:
push:
pull_request:
workflow_dispatch:
env:
CLICOLOR: 1
jobs:
spelling:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v7
with:
submodules: recursive
- name: Spell Check Repo
uses: crate-ci/typos@v1.50.0

31
Dockerfile Normal file
View File

@ -0,0 +1,31 @@
FROM alpine:3.24
ARG TARGETARCH
ARG VINE_TAG=nightly
RUN apk add --no-cache curl ca-certificates && \
case "${TARGETARCH}" in \
"amd64") BIN_ARCH="X64" ;; \
"arm64") BIN_ARCH="ARM64" ;; \
*) echo "Unsupported architecture: ${TARGETARCH}" && exit 1 ;; \
esac && \
curl -fsSL "https://github.com/Pumpkin-MC/Vine/releases/download/${VINE_TAG}/vine-${BIN_ARCH}-Linux-musl" \
-o /usr/local/bin/vine && \
chmod +x /usr/local/bin/vine && \
apk del curl
RUN addgroup -g 2613 vine && \
adduser -u 2613 -G vine -D -h /vine vine && \
chown -R vine:vine /vine
WORKDIR /vine
USER vine:vine
ENV RUST_BACKTRACE=1
EXPOSE 25565
EXPOSE 19132/udp
ENTRYPOINT [ "vine" ]
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD nc -z 127.0.0.1 25565 || exit 1

21
docker-compose.yml Normal file
View File

@ -0,0 +1,21 @@
services:
vine:
# Make sure to change the tag to the vine version you want to use.
# https://github.com/Pumpkin-MC/Vine/pkgs/container/vine/versions?filters%5Bversion_type%5D=tagged
#image: ghcr.io/pumpkin-mc/vine:master
build: .
ports:
- "25565:25565"
- "19132:19132/udp"
volumes:
- ./data:/vine
# Use the following, if you want to make your config immutable
#- ./vine.toml:/vine/vine.toml:ro
security_opt:
- no-new-privileges:true
restart: unless-stopped
stdin_open: true
tty: true
cap_drop:
- ALL
read_only: false

73
egg-vine.json Normal file
View File

@ -0,0 +1,73 @@
{
"_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO",
"meta": {
"version": "PTDL_v2",
"update_url": null
},
"exported_at": "2026-09-08T18:00:00+00:00",
"name": "Vine",
"author": "lilalexmed@proton.me",
"description": "Vine is a high-performance, lightweight, multi-version Minecraft proxy built entirely in Rust, offering sub-millisecond packet routing, WASM plugin support, and modern protocol compatibility.",
"features": null,
"docker_images": {
"Alpine": "ghcr.io/pterodactyl/yolks:alpine",
"Debian": "ghcr.io/pterodactyl/yolks:debian"
},
"file_denylist": [],
"startup": "./vine",
"config": {
"files": "{\r\n \"vine.toml\": {\r\n \"parser\": \"file\",\r\n \"find\": {\r\n \"bind_address = \\\"0.0.0.0:25565\\\"\": \"bind_address = \\\"0.0.0.0:{{server.build.default.port}}\\\"\"\r\n }\r\n }\r\n}",
"startup": "{\r\n \"done\": \"Vine Minecraft Proxy is listening on\"\r\n}",
"logs": "{}",
"stop": "stop"
},
"scripts": {
"installation": {
"script": "#!/bin/ash\r\nset -eux\r\n\r\napk add --no-cache curl jq\r\nmkdir -p /mnt/server\r\ncd /mnt/server\r\n\r\nARCH=$(uname -m)\r\ncase \"$ARCH\" in\r\n x86_64|amd64)\r\n VINE_ARCH=\"X64\"\r\n ;;\r\n aarch64|arm64)\r\n VINE_ARCH=\"ARM64\"\r\n ;;\r\n *)\r\n echo \"Unsupported architecture: $ARCH\"\r\n exit 1\r\n ;;\r\nesac\r\n\r\nBINARY_NAME=\"vine-${VINE_ARCH}-Linux-musl\"\r\n\r\nif [ -n \"${DOWNLOAD_URL:-}\" ]; then\r\n URL=\"$DOWNLOAD_URL\"\r\nelif [ \"${VERSION:-nightly}\" = \"latest\" ] || [ \"${VERSION:-nightly}\" = \"nightly\" ]; then\r\n URL=\"https://github.com/${GITHUB_PACKAGE:-Pumpkin-MC/Vine}/releases/download/nightly/${BINARY_NAME}\"\r\nelse\r\n URL=\"https://github.com/${GITHUB_PACKAGE:-Pumpkin-MC/Vine}/releases/download/${VERSION}/${BINARY_NAME}\"\r\nfi\r\n\r\necho \"Downloading Vine from: $URL\"\r\ncurl -fsSL \"$URL\" -o /mnt/server/vine\r\nchmod +x /mnt/server/vine\r\n\r\n# Quickly start and kill proxy to generate initial configuration if not present\r\nif [ ! -f /mnt/server/vine.toml ]; then\r\n timeout 2 ./vine || true\r\nfi",
"container": "ghcr.io/pterodactyl/installers:alpine",
"entrypoint": "ash"
}
},
"variables": [
{
"name": "Rust backtrace",
"description": "\"0\" -> No backtrace is generated. This is the default behavior.\r\n\"1\" -> Generates a minimal backtrace showing only relevant information about where the panic occurred.\r\n\"full\" -> Generates a detailed backtrace, showing all frames leading to the panic, including functions in the Rust standard library and dependencies.",
"env_variable": "RUST_BACKTRACE",
"default_value": "1",
"user_viewable": true,
"user_editable": true,
"rules": "required|in:1,0,full",
"field_type": "text"
},
{
"name": "Vine Version",
"description": "The version of Vine to install. Options: 'nightly', 'latest', or a specific release tag (e.g. 'v0.1.0').\r\nScope: installation",
"env_variable": "VERSION",
"default_value": "nightly",
"user_viewable": true,
"user_editable": true,
"rules": "required|string|max:64",
"field_type": "text"
},
{
"name": "GitHub Repository",
"description": "GitHub repository in 'owner/repo' format to download releases from. Useful if you are using a fork.\r\nScope: installation",
"env_variable": "GITHUB_PACKAGE",
"default_value": "Pumpkin-MC/Vine",
"user_viewable": true,
"user_editable": true,
"rules": "required|string|max:128",
"field_type": "text"
},
{
"name": "Direct Download URL",
"description": "Direct URL to download the Vine binary from. If provided, this overrides VERSION and GITHUB_PACKAGE.\r\nScope: installation",
"env_variable": "DOWNLOAD_URL",
"default_value": "",
"user_viewable": true,
"user_editable": true,
"rules": "nullable|string",
"field_type": "text"
}
]
}

10
typos.toml Normal file
View File

@ -0,0 +1,10 @@
[files]
extend-exclude = ["assets/"]
[default.extend-words]
handled = "handled"
received = "received"
ba = "ba"
fo = "fo"
undescore = "undescore"
replaceables = "replaceables"