Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions .github/workflows/build_and_release_for_pluton.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
name: "Build and release for pluton"

on:
workflow_dispatch:
inputs:
pluton_version:
description: 'Pluton version tag (e.g., "latest" or commit sha)'
required: false
default: 'latest'
schedule:
- cron: '0 2 * * 1' # Weekly
push:
branches:
- main
paths:
- 'Apps/pluton/**'
- '.github/workflows/build_and_release_for_pluton.yaml'

env:
IMAGE_NAME: bigbeartechworld/big-bear-pluton
DOCKERFILE_PATH: Apps/pluton

jobs:
check-pluton-release:
name: Check Pluton Latest Release
runs-on: ubuntu-latest
outputs:
pluton_version: ${{ steps.get_version.outputs.version }}
should_build: ${{ steps.check_build.outputs.should_build }}
steps:
- name: Get Latest Pluton Commit
id: get_version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ github.event.inputs.pluton_version }}" != "latest" ]; then
VERSION="${{ github.event.inputs.pluton_version }}"
else
# Get latest commit SHA from GitHub API with validation
RESPONSE=$(curl -s -w "%{http_code}" https://api.github.com/repos/plutonhq/pluton/commits/main)
HTTP_CODE=${RESPONSE: -3}
CONTENT=${RESPONSE:0:${#RESPONSE}-3}

if [ "$HTTP_CODE" != "200" ]; then
echo "Error: GitHub API returned status $HTTP_CODE"
exit 1
fi

if [ -z "$CONTENT" ]; then
echo "Error: Empty response from GitHub API"
exit 1
fi

VERSION=$(echo "$CONTENT" | jq -r '.sha' | cut -c1-7)

if [[ ! "$VERSION" =~ ^[0-9a-f]{7}$ ]]; then
echo "Error: Invalid version format received: $VERSION"
exit 1
fi
fi
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "Pluton version: ${VERSION}"

- name: Check if image exists
id: check_image
if: github.event_name == 'schedule'
run: |
VERSION="${{ steps.get_version.outputs.version }}"
# Check if image with this tag already exists
CODE=$(curl --silent --max-time 10 -o /dev/null -w "%{http_code}" https://hub.docker.com/v2/repositories/${{ env.IMAGE_NAME }}/tags/${VERSION})

if [ "$CODE" = "000" ] || [ -z "$CODE" ]; then
echo "Error: Failed to check Docker Hub (curl error)"
echo "exists=unknown" >> "$GITHUB_OUTPUT"
exit 1
fi

if [ "$CODE" == "200" ]; then
echo "Image exists"
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "Image does not exist"
echo "exists=false" >> "$GITHUB_OUTPUT"
fi

- name: Check if Build Needed
id: check_build
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ] || [ "${{ github.event_name }}" == "push" ]; then
echo "should_build=true" >> "$GITHUB_OUTPUT"
elif [ "${{ steps.check_image.outputs.exists }}" == "false" ]; then
echo "should_build=true" >> "$GITHUB_OUTPUT"
else
echo "should_build=false" >> "$GITHUB_OUTPUT"
fi

build-and-push:
name: Build and Push
needs: check-pluton-release
if: needs.check-pluton-release.outputs.should_build == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest
type=raw,value=${{ needs.check-pluton-release.outputs.pluton_version }}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ${{ env.DOCKERFILE_PATH }}
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
123 changes: 123 additions & 0 deletions Apps/pluton/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Stage 0: Clone
FROM alpine:latest as source
RUN apk add --no-cache git
WORKDIR /src
ARG PLUTON_VERSION=1a2986b2d59ecd49e82a19df4afa8066b70a20af
RUN git clone https://github.com/plutonhq/pluton.git . && \
git checkout ${PLUTON_VERSION}

ARG APP_VERSION=0.0.1
ARG RESTIC_VERSION=0.18.1
ARG RCLONE_VERSION=1.71.2

# Stage 1: Dependencies
FROM node:24-alpine AS deps
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /workspace
COPY --from=source /src/package.json /src/pnpm-workspace.yaml /src/pnpm-lock.yaml /src/turbo.json ./
COPY --from=source /src/frontend/package.json ./frontend/package.json
COPY --from=source /src/backend/package.json ./backend/package.json
RUN pnpm install --frozen-lockfile

# Stage 2: Frontend Builder
FROM node:24-alpine AS frontend-builder
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /workspace
COPY --from=deps /workspace/node_modules ./node_modules
COPY --from=deps /workspace/pnpm-lock.yaml ./
COPY --from=source /src ./
RUN pnpm install --frozen-lockfile
RUN pnpm --filter @plutonhq/core-frontend build

# Stage 3: Backend Builder
FROM node:24-alpine AS backend-builder
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /workspace
COPY --from=deps /workspace/node_modules ./node_modules
COPY --from=deps /workspace/pnpm-lock.yaml ./
COPY --from=source /src ./
COPY --from=frontend-builder /workspace/frontend/dist ./frontend/dist
RUN pnpm install --frozen-lockfile
RUN pnpm --filter @plutonhq/core-backend build
RUN pnpm --filter @plutonhq/core-backend deploy --prod --legacy /deploy

# Stage 4: Runner
FROM node:24-alpine AS runner
Comment thread
dragonfire1119 marked this conversation as resolved.
WORKDIR /app
RUN apk add --no-cache curl ca-certificates fuse3 bash su-exec

# Create data directory and set permissions
RUN mkdir -p /data /data/logs /data/db /data/config && \
chown -R node:node /data /app

# Download specific versions of restic and rclone binaries
ARG TARGETARCH
ARG RESTIC_VERSION=0.18.1
ARG RCLONE_VERSION=1.71.2

# Checksums
ARG RESTIC_SHA256_AMD64=680838f19d67151adba227e1570cdd8af12c19cf1735783ed1ba928bc41f363d
ARG RESTIC_SHA256_ARM64=87f53fddde38764095e9c058a3b31834052c37e5826d2acf34e18923c006bd45
ARG RCLONE_SHA256_AMD64=ab9fa5877cee91c64fdfd61a27028a458cf618b39259e5c371dc2ec34a12e415
ARG RCLONE_SHA256_ARM64=e2e2efc7ed143026352d60216ef0d46d3fa4fe9d647eff1bd929e6fea498e6f1

RUN echo "Building for architecture: ${TARGETARCH}" && \
echo "Installing restic v${RESTIC_VERSION} and rclone v${RCLONE_VERSION}" && \
# Download restic
if [ "${TARGETARCH}" = "amd64" ]; then \
wget -q "https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_amd64.bz2" -O /tmp/restic.bz2; \
echo "${RESTIC_SHA256_AMD64} /tmp/restic.bz2" | sha256sum -c -; \
elif [ "${TARGETARCH}" = "arm64" ]; then \
wget -q "https://github.com/restic/restic/releases/download/v${RESTIC_VERSION}/restic_${RESTIC_VERSION}_linux_arm64.bz2" -O /tmp/restic.bz2; \
echo "${RESTIC_SHA256_ARM64} /tmp/restic.bz2" | sha256sum -c -; \
fi && \
bunzip2 /tmp/restic.bz2 && \
mv /tmp/restic /usr/local/bin/restic && \
chmod +x /usr/local/bin/restic && \
# Download rclone
if [ "${TARGETARCH}" = "amd64" ]; then \
wget -q "https://downloads.rclone.org/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-amd64.zip" -O /tmp/rclone.zip; \
echo "${RCLONE_SHA256_AMD64} /tmp/rclone.zip" | sha256sum -c -; \
elif [ "${TARGETARCH}" = "arm64" ]; then \
wget -q "https://downloads.rclone.org/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-arm64.zip" -O /tmp/rclone.zip; \
echo "${RCLONE_SHA256_ARM64} /tmp/rclone.zip" | sha256sum -c -; \
fi && \
apk add --no-cache unzip && \
unzip -q /tmp/rclone.zip -d /tmp && \
mv /tmp/rclone-*/rclone /usr/local/bin/rclone && \
chmod +x /usr/local/bin/rclone && \
rm -rf /tmp/rclone* && \
apk del unzip && \
# Verify installations
unset RESTIC_VERSION RCLONE_VERSION && \
restic version && \
rclone version

# Create Pluton wrapper scripts for rclone and restic
RUN echo '#!/bin/sh' > /usr/local/bin/prclone && \
echo '# Pluton Wrapper for rclone' >> /usr/local/bin/prclone && \
echo 'exec /usr/local/bin/rclone --config /data/config/rclone.conf "$@"' >> /usr/local/bin/prclone && \
chmod +x /usr/local/bin/prclone && \
echo '#!/bin/sh' > /usr/local/bin/prestic && \
echo '# Pluton Wrapper for restic' >> /usr/local/bin/prestic && \
echo 'export RCLONE_CONFIG=/data/config/rclone.conf' >> /usr/local/bin/prestic && \
echo 'exec /usr/local/bin/restic -o rclone.program=/usr/local/bin/rclone "$@"' >> /usr/local/bin/prestic && \
chmod +x /usr/local/bin/prestic

COPY --from=backend-builder /deploy ./
COPY --from=source /src/backend/drizzle ./drizzle
COPY --from=backend-builder /workspace/backend/loader.mjs ./loader.mjs
COPY --from=frontend-builder /workspace/frontend/dist ./public
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ARG APP_VERSION
ENV NODE_ENV=production \
IS_DOCKER=true \
SERVER_PORT=5173 \
APP_VERSION=${APP_VERSION}

EXPOSE 5173

ENTRYPOINT ["/entrypoint.sh"]
CMD ["node", "--loader", "./loader.mjs", "dist/index.js"]
1 change: 1 addition & 0 deletions Apps/pluton/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.0.1
29 changes: 29 additions & 0 deletions Apps/pluton/compose/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
services:
big-bear-pluton:
build: ..
image: bigbeartechworld/big-bear-pluton:latest
container_name: big-bear-pluton
restart: unless-stopped
ports:
- "5173:5173"
environment:
SECRET: ${SECRET:-CHANGE_ME_SECRET_KEY_123}
ENCRYPTION_KEY: ${ENCRYPTION_KEY:-CHANGE_ME_ENCRYPTION_KEY_123}
APIKEY: ${APIKEY:-CHANGE_ME_API_KEY_123}
USER_NAME: ${USER_NAME:-admin}
USER_PASSWORD: ${USER_PASSWORD:-password}
APP_TITLE: ${APP_TITLE:-Pluton}
APP_URL: ${APP_URL:-http://localhost:5173}
SERVER_PORT: ${SERVER_PORT:-5173}
volumes:
- big_bear_pluton_data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5173/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s

volumes:
big_bear_pluton_data:
driver: local
44 changes: 44 additions & 0 deletions Apps/pluton/config/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "Pluton",
"description": "A modern, self-hosted backup solution for secure, encrypted backups across local and cloud storage.",
"version": "latest",
"category": "Backup",
"tags": ["backup", "restic", "rclone", "self-hosted", "encrypted"],
"platform": "linux/amd64,linux/arm64",
"logo": "https://pluton.b-cdn.net/logo.png",
"documentation": "https://docs.usepluton.com/",
"github": "https://github.com/plutonhq/pluton",
"ports": {
"5173/tcp": "Pluton Web UI"
},
"volumes": {
"/data": "Pluton data directory"
},
"environment": {
"SECRET": {
"description": "Master encryption key for securing sensitive data (min 12 chars)",
"default": "CHANGE_ME_SECRET_KEY_123"
},
"ENCRYPTION_KEY": {
"description": "Encryption key for restic/rclone Snapshot encryption (min 12 chars)",
"default": "CHANGE_ME_ENCRYPTION_KEY_123"
},
"APIKEY": {
"description": "API authentication key",
"default": "CHANGE_ME_API_KEY_123"
},
"USER_NAME": {
"description": "Admin username for login",
"default": "admin"
},
"USER_PASSWORD": {
"description": "Admin password for login",
"default": "password"
}
},
"notes": [
"Default credentials are admin:password - CHANGE THESE IN PRODUCTION!",
"Access the Pluton UI at http://localhost:5173",
"Ensure you set strong secrets for SECRET, ENCRYPTION_KEY, and APIKEY."
]
}
7 changes: 7 additions & 0 deletions Apps/pluton/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/sh

# Fix permissions for /data
chown -R node:node /data

# Execute the command as node user
exec su-exec node "$@"
34 changes: 34 additions & 0 deletions Apps/pluton/scripts/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/bash

# Build script for Pluton Docker image
# This script builds the Pluton Docker image for multiple architectures

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Configuration
IMAGE_NAME="bigbeartechworld/big-bear-pluton"
VERSION=$(cat VERSION)

echo -e "${GREEN}Building Pluton Docker Image${NC}"
echo -e "${YELLOW}Version: ${VERSION}${NC}"

# Check if we are in the right directory
if [ ! -f "Dockerfile" ]; then
echo -e "${RED}Error: Dockerfile not found. Please run this script from the Apps/pluton directory.${NC}"
exit 1
fi

# Build the image
echo -e "${YELLOW}Building image...${NC}"
docker build -t "${IMAGE_NAME}:${VERSION}" -t "${IMAGE_NAME}:latest" .

echo -e "${GREEN}Build complete!${NC}"
echo -e "Tags:"
echo -e " - ${IMAGE_NAME}:${VERSION}"
echo -e " - ${IMAGE_NAME}:latest"