Skip to content

Commit

Permalink
Add package development instructions (#437)
Browse files Browse the repository at this point in the history
  • Loading branch information
dappnodedev authored Sep 11, 2024
1 parent 0acdcf3 commit 37a77f5
Show file tree
Hide file tree
Showing 4 changed files with 349 additions and 0 deletions.
195 changes: 195 additions & 0 deletions docs/dev/package-development/multi-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# Multi-Config Package Development

This guide will walk you through the steps to develop a multi-config Dappnode package, allowing you to create multiple package configurations from a single source. We'll use the Lodestar Generic package as a reference, which builds multiple variants for different networks like Mainnet, Holesky, and Gnosis.

## Step 1: Initialize the Package

Start by running the following command to initialize your multi-variant Dappnode package:

```bash
npx @dappnode/dappnodesdk@latest init --use-variants
```

This will create the following directory structure:

```
.
├── avatar-default.png
├── dappnode_package.json
├── docker-compose.yml
├── Dockerfile
└── package_variants
├── mainnet
│ ├── dappnode_package.json
│ └── docker-compose.yml
└── testnet
├── dappnode_package.json
└── docker-compose.yml
```

## Step 2: Customize your package

### Create a directory for each variant

Each variant of your package will have its own configuration files under `package_variants`. For example, if you have `mainnet` and `testnet` variants, you will find the following:

```
package_variants/
├── mainnet/
│ ├── dappnode_package.json
│ └── docker-compose.yml
└── testnet/
├── dappnode_package.json
└── docker-compose.yml
```

The contents within each variant directory include the fields that differ from one variant to another. When building a specific variant, such as `testnet`, the data from these variant-specific files is merged with the root-level `dappnode_package.json` and `docker-compose.yml`. This ensures that only the necessary variant-specific changes are applied, while the common configuration remains consistent across all variants.

You can add more variants as needed by creating additional directories and files following this structure.

### Customize the Avatar

Replace the default avatar (`avatar-default.png`) with a square `.png` image that represents your package (recommended dimensions: width between 200px and 300px). This image will appear in the Dappnode UI, so it's important to choose one that aligns with your package's branding. The avatar is common for each of the variants.

### Key Changes in `docker-compose.yml` and `dappnode_package.json`

In the root-level `docker-compose.yml` and `dappnode_package.json`, specify the general configuration that applies across all variants.

Each variant in `package_variants` will have its own `docker-compose.yml` and `dappnode_package.json`, tailored to the network or environment it is intended for.

For example, `package_variants/mainnet/docker-compose.yml` could define services using `NETWORK=mainnet`, while `package_variants/testnet/docker-compose.yml` would use `NETWORK=testnet`.

Example of a variant-specific `dappnode_package.json`:

```json
{
"name": "test-mainnet.public.dappnode.eth",
"version": "0.1.0",
"type": "service"
}
```

And the corresponding `docker-compose.yml`:

```json
version: "3.5"
services:
test:
build:
args:
NETWORK: mainnet

```

### Simplified Dockerfile

Here’s an example of the `Dockerfile` used for the Lodestar Generic package. This `Dockerfile` is responsible for configuring the Lodestar Ethereum client and handling multiple variants.

```Dockerfile
ARG UPSTREAM_VERSION

FROM chainsafe/lodestar:${UPSTREAM_VERSION}

ARG NETWORK
ARG STAKER_SCRIPTS_VERSION

# Additional envs

ENV STAKER_SCRIPTS_URL=https://github.com/dappnode/staker-package-scripts/releases/download/${STAKER_SCRIPTS_VERSION}

COPY entrypoint.sh /usr/local/bin/entrypoint.sh

# These scripts provide useful tools for the entrypoint
ADD ${STAKER_SCRIPTS_URL}/consensus_tools.sh /etc/profile.d/
RUN chmod +rx /etc/profile.d/consensus_tools.sh

# Additional commands or package installation

# This environment variable sets the variant (e.g., mainnet, testnet)
ENV NETWORK=${NETWORK}

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
```

### Simplified entrypoint

The `entrypoint.sh` script for the Lodestar client manages different network setups (e.g., Holesky, Gnosis, Mainnet) and configures runtime flags accordingly. Here’s a simplified version:

```bash
#!/bin/sh

SUPPORTED_NETWORKS="gnosis holesky mainnet"
MEVBOOST_FLAG_KEY="--builder"
CLIENT="lodestar"

# Load the tools scripts
. /etc/profile

# Get the necessary environment variables for the beacon node
ENGINE_URL="http://execution.${NETWORK}.staker.dappnode:8551"
VALID_FEE_RECIPIENT=$(get_valid_fee_recipient "${FEE_RECIPIENT}")
MEVBOOST_FLAG=$(get_mevboost_flag "${NETWORK}" "${MEVBOOST_FLAG_KEY}")

JWT_SECRET=$(get_jwt_secret_by_network "${NETWORK}")
echo "${JWT_SECRET}" >"${JWT_FILE_PATH}"

# Start the beacon node with the appropriate flags
echo "[INFO - entrypoint] Running beacon node"

FLAGS="beacon \
--network=${NETWORK} \
--suggestedFeeRecipient=${VALID_FEE_RECIPIENT} \
--jwt-secret=${JWT_FILE_PATH} \
--execution.urls=${ENGINE_URL} \
# ... Additional flags here
--logFileDailyRotate=5 $MEVBOOST_FLAG $EXTRA_OPTS"

# Run the Lodestar client with the specified flags
exec ${CLIENT_BIN} $FLAGS
```

### Prometheus and Grafana

In multi-variant packages, you can use a common Grafana dashboard located at the root level, while each variant can have its own Prometheus targets. These are stored in variant-specific directories like `package_variants/gnosis/prometheus-targets.json`.

By following this guide, you’ll be able to create multi-variant packages that support different networks or configurations within a single source code base.

## Step 3: Build the packages

Once your variants are configured, build any of them using the following command:

```bash
npx @dappnode/dappnodesdk@latest build --variants=<comma-separated-list-of-variants>
```

If you want to build all variants at the same time, you can use:

```bash
npx @dappnode/dappnodesdk@latest build --all-variants
```

These commands will package your services, making them ready for installation on Dappnode machines.

## Step 4: Publish the Packages

Once you have built your variants, you can choose to publish them. While publishing is optional if you only want to use the package locally, it is required if you want your package to be available in the public Dappstore for other users to discover and install.

The publish command allows you to specify which variants you want to publish, using the same `--variants` flag as the build command. You can publish one or more variants as follows:

```bash
npx @dappnode/dappnodesdk@latest publish --type=<patch/minor/major> --variants=<comma-separated-list-of-variants> --eth-provider=<your ETH RPC> --content-provider=<your IPFS API> --developer-address=<the address to sign>
```

If you want to generate all variants publish links at once, you can use the following command:

```bash
npx @dappnode/dappnodesdk@latest publish --type=patch --all-variants --eth-provider=https://your-eth-node --content-provider=https://your-ipfs-api --developer-address=0xYourAddress
```

After running the publish command, you will receive a link to sign the transaction to complete the publishing process.

### Automating Publication with GitHub Actions

If your package source code is hosted on GitHub, you can automate the publishing process by setting up GitHub Actions. Use the workflows [here](/docs/dev/github-actions.md) to integrate publishing workflows, ensuring your package builds and publishes automatically.

By following these steps, you'll be able to develop, build, and publish multi-variant packages on Dappnode, making it easier to support multiple configurations from a single codebase.
17 changes: 17 additions & 0 deletions docs/dev/package-development/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Package Development

Welcome to the **Package Development** section! This guide will help you create your own Dappnode packages. In Dappnode, packages are applications that you can download and install on your Dappnode machine, allowing you to enhance its functionality.

### Types of Package Repositories

There are two main types of package repositories:

- **Standard Package Repository**: Used to generate a single package, tailored for a specific configuration.
- **Multi-Configuration (Generic) Package Repository**: Used to generate multiple packages with varying configurations, such as different networks or client setups. For example, a multi-configuration repository could be used to build packages for both **Holesky Nethermind** and **Mainnet Nethermind**, which differ in their configurations.

### Developing Packages

- **Single-Configuration Package**: If you want to develop a package with a single configuration, like [Rotki](https://github.com/dappnode/DAppNodePackage-rotki), follow the instructions [here](/docs/dev/package-development/single-configuration.md).
- **Multi-Configuration Package**: For packages with multiple configurations, like [Lodestar](https://github.com/dappnode/DAppNodePackage-lodestar-generic), refer to [this guide](/docs/dev/package-development/multi-configuration.md).

By following these guides, you'll be able to create and contribute your own packages to the Dappnode ecosystem.
116 changes: 116 additions & 0 deletions docs/dev/package-development/single-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Single-Variant Package Development

This guide will walk you through the steps to develop a single-variant Dappnode package using a simple example. We'll start with initializing a basic package and progress to a more complex setup, using the Holesky Geth package as a reference.

## Step 1: Initialize the Package

Start by running the following command to initialize your Dappnode package:

```bash
npx @dappnode/dappnodesdk@latest init
```

This will create the following directory structure:

```
.
├── avatar-default.png
├── dappnode_package.json
├── docker-compose.yml
└── Dockerfile
```

## Step 2: Customize your Package

### Create a Directory for Container Files

For more complex packages, such as the Holesky Geth package, it is recommended to create a directory (e.g., `geth`) that will contain everything that needs to go inside each Docker container corresponding to a service in the compose file. This includes the `Dockerfile`, an `entrypoint.sh` script, and any security or configuration files. Example:

```
geth/
├── Dockerfile
├── entrypoint.sh
└── security/
└── jwtsecret.hex
```

### Customize the avatar

Replace the default avatar (`avatar-default.png`) with a square `.png` image that represents your package (recommended dimensions: width between 200px and 300px). This image will appear in the Dappnode UI, so it's important to choose one that aligns with your package's branding.

### Key Changes in `docker-compose.yml` and `dappnode_package.json`

In the `docker-compose.yml`, modify the services section to:

- Reference the newly created directory (`geth` in this example) where the `Dockerfile` and other container files are stored.
- Set up environment variables, ports, and volumes relevant to your service.

In the `dappnode_package.json`, update fields such as:

- `name`, `version`, `description`, and `author` to reflect your package.
- Add relevant `categories`, `architectures`, and exposed services (e.g., API endpoints).

### Simplified Dockerfile

Here’s a simplified version of the Dockerfile inside the `geth` directory:

```Dockerfile
ARG UPSTREAM_VERSION

FROM ethereum/client-go:${UPSTREAM_VERSION}

COPY /security /security
COPY entrypoint.sh /usr/local/bin/entrypoint.sh

# Additional commands or package installation

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
```

### Simplified entrypoint.sh

The `entrypoint.sh` script is responsible for setting up the environment and running the application. Here’s a simplified version:

```bash
#!/bin/sh

# Additional logic related to JWT token

# Start the Geth process (add any flags you consider relevant)
exec geth --authrpc.jwtsecret ${JWT_PATH} ${EXTRA_FLAGS}
```

## Step 3: Build the package

Once all the necessary customizations are made, you can build your package by running the following command while connected to your Dappnode box:

```bash
npx @dappnode/dappnodesdk@latest build
```

This will package your service, making it ready for installation on a Dappnode machine. Once you get the package hash, you just have to paste it into the Dappstore search bar to download it.

## Step 4: Publish the package

Publishing the package is optional; you can install and use the package locally after building it. However, if you want the package to be available in the public Dappstore for other users to discover and install, publishing is required. To publish the package, start by running the following command:

```bash
npx @dappnode/dappnodesdk@latest publish --type=<patch/minor/major> --eth-provider=<your ETH RPC> --content-provider=<your IPFS API> --developer-address=<the address to sign>
```

This command will return a link where you can perform the transaction to publish the package.

### Naming conventions for packages

All package names must follow this convention:

```
<name>.<public/dnp>.dappnode.eth
```

- DNP: This suffix is reserved for official Dappnode packages and must be approved by the Dappnode Association.
- Public: This suffix is for community-contributed packages that can be published by anyone.

### Automating Publication with GitHub Actions

If the package source code is hosted on GitHub, you can automate the publishing process by setting up GitHub Actions. Use the workflows [here](/docs/dev/github-actions.md) to integrate publishing workflows.
21 changes: 21 additions & 0 deletions sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,27 @@ const sidebars = {
},
],
},
{
type: "category",
label: "Package Development",
items: [
{
type: "doc",
label: "Overview",
id: "dev/package-development/overview",
},
{
type: "doc",
label: "Single Configuration",
id: "dev/package-development/single-configuration",
},
{
type: "doc",
label: "Multi-Configuration",
id: "dev/package-development/multi-configuration",
},
],
},
{
type: "doc",
label: "Github Actions",
Expand Down

0 comments on commit 37a77f5

Please sign in to comment.