From 59b08cffa468f99c4dd016f4992eb97f6467fbf2 Mon Sep 17 00:00:00 2001 From: Sergey Ivanov Date: Wed, 19 Nov 2025 10:41:38 +0500 Subject: [PATCH 1/3] Refactor scripting handling and add portainer pipeline --- .github/workflows/portainer-deploy.yml | 89 +++++++++++ README.md | 10 ++ .../holyway/botplatform/scripting/Script.java | 7 +- .../scripting/ScriptCompilationException.java | 22 +++ .../scripting/ScriptCompilerImpl.java | 9 +- .../scripting/ScriptContextFactory.java | 27 ++++ .../botplatform/scripting/ScriptRegistry.java | 59 ++++++++ .../processor/ScriptMessageProcessor.java | 140 ++++++++---------- .../scripting/ScriptCompilerImplTest.java | 40 +++++ .../scripting/ScriptRegistryTest.java | 56 +++++++ 10 files changed, 374 insertions(+), 85 deletions(-) create mode 100644 .github/workflows/portainer-deploy.yml create mode 100644 src/main/java/ru/holyway/botplatform/scripting/ScriptCompilationException.java create mode 100644 src/main/java/ru/holyway/botplatform/scripting/ScriptContextFactory.java create mode 100644 src/main/java/ru/holyway/botplatform/scripting/ScriptRegistry.java create mode 100644 src/test/java/ru/holyway/botplatform/scripting/ScriptCompilerImplTest.java create mode 100644 src/test/java/ru/holyway/botplatform/scripting/ScriptRegistryTest.java diff --git a/.github/workflows/portainer-deploy.yml b/.github/workflows/portainer-deploy.yml new file mode 100644 index 0000000..0886ca8 --- /dev/null +++ b/.github/workflows/portainer-deploy.yml @@ -0,0 +1,89 @@ +name: Build and Deploy to Portainer + +on: + push: + branches: + - main + - master + workflow_dispatch: {} + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + TAG: latest + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '17' + cache: maven + + - name: Build application + run: ./mvnw -B package -DskipTests + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }} + + - name: Deploy stack via Portainer API + env: + PORTAINER_URL: ${{ secrets.PORTAINER_URL }} + PORTAINER_USERNAME: ${{ secrets.PORTAINER_USERNAME }} + PORTAINER_PASSWORD: ${{ secrets.PORTAINER_PASSWORD }} + PORTAINER_STACK_ID: ${{ secrets.PORTAINER_STACK_ID }} + run: | + python - <<'PY' + import json + import os + import sys + from urllib import request + + url = os.environ.get("PORTAINER_URL") + username = os.environ.get("PORTAINER_USERNAME") + password = os.environ.get("PORTAINER_PASSWORD") + stack_id = os.environ.get("PORTAINER_STACK_ID") + + if not all([url, username, password, stack_id]): + sys.exit("Portainer secrets are not configured") + + data = json.dumps({"Username": username, "Password": password}).encode() + req = request.Request(f"{url}/api/auth", data=data, headers={"Content-Type": "application/json"}) + with request.urlopen(req) as resp: + token = json.loads(resp.read().decode()).get("jwt") + + if not token: + sys.exit("Failed to obtain Portainer JWT token") + + deploy_request = request.Request( + f"{url}/api/stacks/{stack_id}/deploy", + data=json.dumps({"prune": True, "pullImage": True}).encode(), + headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, + method="POST", + ) + + with request.urlopen(deploy_request) as resp: + print(f"Deploy response status: {resp.status}") + print(resp.read().decode()) + PY diff --git a/README.md b/README.md index c980c2f..6dd340a 100644 --- a/README.md +++ b/README.md @@ -12,3 +12,13 @@ For example you can write script to remove all messages with word `F*ck` in the * Some another minor features like random meme generation and notifying all participants in the chat by @all. I didn't try to write beautiful code so... it looks like a shit :) + +## CI/CD + +The repository now contains the workflow `.github/workflows/portainer-deploy.yml` that builds the +application, publishes an image to GitHub Container Registry and triggers a Portainer stack +redeploy. Configure the following secrets before enabling the workflow: + +- `PORTAINER_URL` – base URL of the Portainer instance (for example `https://portainer.example.com`). +- `PORTAINER_USERNAME` / `PORTAINER_PASSWORD` – credentials with rights to deploy the target stack. +- `PORTAINER_STACK_ID` – identifier of the stack to redeploy. diff --git a/src/main/java/ru/holyway/botplatform/scripting/Script.java b/src/main/java/ru/holyway/botplatform/scripting/Script.java index 40c46cd..eff5651 100644 --- a/src/main/java/ru/holyway/botplatform/scripting/Script.java +++ b/src/main/java/ru/holyway/botplatform/scripting/Script.java @@ -1,5 +1,6 @@ package ru.holyway.botplatform.scripting; +import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import java.util.Objects; @@ -9,6 +10,7 @@ import java.util.function.Function; import java.util.function.Predicate; +@Slf4j public class Script implements Comparable