diff --git a/.github/workflows/npm-telegram-beta-e2e.yml b/.github/workflows/npm-telegram-beta-e2e.yml index ee4eaddbc11..f1a53c81858 100644 --- a/.github/workflows/npm-telegram-beta-e2e.yml +++ b/.github/workflows/npm-telegram-beta-e2e.yml @@ -52,18 +52,18 @@ jobs: uses: actions/github-script@v8 with: script: | - const allowed = new Set(["admin", "write"]); + const allowedRoles = new Set(["admin", "maintain"]); const { owner, repo } = context.repo; const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ owner, repo, username: context.actor, }); - const permission = data.permission; - core.info(`Actor ${context.actor} permission: ${permission}`); - if (!allowed.has(permission)) { + const role = data.role_name ?? data.permission; + core.info(`Actor ${context.actor} role: ${role}`); + if (!allowedRoles.has(role)) { core.setFailed( - `Workflow requires write/admin access. Actor "${context.actor}" has "${permission}".`, + `Workflow requires maintainer/admin access. Actor "${context.actor}" has "${role}".`, ); } diff --git a/scripts/e2e/npm-telegram-live-docker.sh b/scripts/e2e/npm-telegram-live-docker.sh index 2dd05bacb01..433c8a6e09a 100755 --- a/scripts/e2e/npm-telegram-live-docker.sh +++ b/scripts/e2e/npm-telegram-live-docker.sh @@ -50,8 +50,12 @@ docker_e2e_build_or_reuse "$IMAGE_NAME" npm-telegram-live "$ROOT_DIR/scripts/e2e mkdir -p "$ROOT_DIR/.artifacts/qa-e2e" run_log="$(mktemp "${TMPDIR:-/tmp}/openclaw-npm-telegram-live.XXXXXX")" +npm_prefix_host="$(mktemp -d "$ROOT_DIR/.artifacts/qa-e2e/npm-telegram-live-prefix.XXXXXX")" credential_source="$(resolve_credential_source)" credential_role="$(resolve_credential_role)" +if [ -z "$credential_role" ] && [ -n "${CI:-}" ] && [ "$credential_source" = "convex" ]; then + credential_role="ci" +fi docker_env=( -e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 @@ -109,15 +113,15 @@ done echo "Running published npm Telegram live Docker E2E ($PACKAGE_SPEC)..." if ! docker run --rm \ - "${docker_env[@]}" \ - -v "$ROOT_DIR/.artifacts:/app/.artifacts" \ + -e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \ + -e OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC="$PACKAGE_SPEC" \ + -v "$npm_prefix_host:/npm-global" \ -i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF' set -euo pipefail -export HOME="$(mktemp -d "/tmp/openclaw-npm-telegram-live.XXXXXX")" -export NPM_CONFIG_PREFIX="$HOME/.npm-global" +export HOME="$(mktemp -d "/tmp/openclaw-npm-telegram-install.XXXXXX")" +export NPM_CONFIG_PREFIX="/npm-global" export PATH="$NPM_CONFIG_PREFIX/bin:$PATH" -export OPENCLAW_NPM_TELEGRAM_REPO_ROOT="/app" package_spec="${OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC:?missing OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC}" if [[ ! "$package_spec" =~ ^openclaw@(beta|latest|[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(-[1-9][0-9]*|-beta\.[1-9][0-9]*)?)$ ]]; then @@ -127,6 +131,31 @@ fi echo "Installing ${package_spec}..." npm install -g "$package_spec" --no-fund --no-audit +command -v openclaw +openclaw --version +EOF +then + cat "$run_log" + rm -f "$run_log" + rm -rf "$npm_prefix_host" + exit 1 +fi + +cat "$run_log" +>"$run_log" + +if ! docker run --rm \ + "${docker_env[@]}" \ + -v "$ROOT_DIR/.artifacts:/app/.artifacts" \ + -v "$npm_prefix_host:/npm-global" \ + -i "$IMAGE_NAME" bash -s >"$run_log" 2>&1 <<'EOF' +set -euo pipefail + +export HOME="$(mktemp -d "/tmp/openclaw-npm-telegram-runtime.XXXXXX")" +export NPM_CONFIG_PREFIX="/npm-global" +export PATH="$NPM_CONFIG_PREFIX/bin:$PATH" +export OPENCLAW_NPM_TELEGRAM_REPO_ROOT="/app" + command -v openclaw openclaw --version @@ -136,9 +165,11 @@ EOF then cat "$run_log" rm -f "$run_log" + rm -rf "$npm_prefix_host" exit 1 fi cat "$run_log" rm -f "$run_log" +rm -rf "$npm_prefix_host" echo "published npm Telegram live Docker E2E passed ($PACKAGE_SPEC)" diff --git a/test/scripts/npm-telegram-live.test.ts b/test/scripts/npm-telegram-live.test.ts index 71c5ce40cb2..0154ff6a5ee 100644 --- a/test/scripts/npm-telegram-live.test.ts +++ b/test/scripts/npm-telegram-live.test.ts @@ -6,6 +6,7 @@ import { __testing } from "../../scripts/e2e/npm-telegram-live-runner.ts"; const TEST_DIR = path.dirname(fileURLToPath(import.meta.url)); const DOCKER_SCRIPT_PATH = path.resolve(TEST_DIR, "../../scripts/e2e/npm-telegram-live-docker.sh"); +const WORKFLOW_PATH = path.resolve(TEST_DIR, "../../.github/workflows/npm-telegram-beta-e2e.yml"); describe("npm Telegram live Docker E2E", () => { it("supports npm-specific Convex credential aliases", () => { @@ -28,6 +29,27 @@ describe("npm Telegram live Docker E2E", () => { expect(script).toContain('printf "convex"'); }); + it("installs the npm package before forwarding runtime secrets", () => { + const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8"); + const installRun = script.slice( + script.indexOf('echo "Running published npm Telegram live Docker E2E'), + script.indexOf('cat "$run_log"\n>"$run_log"'), + ); + + expect(installRun).toContain('npm install -g "$package_spec" --no-fund --no-audit'); + expect(installRun).not.toContain('"${docker_env[@]}"'); + expect(script).toContain('if [ -z "$credential_role" ] && [ -n "${CI:-}" ]'); + expect(script).toContain('credential_role="ci"'); + }); + + it("limits the manual npm beta workflow to maintainer-level actors", () => { + const workflow = readFileSync(WORKFLOW_PATH, "utf8"); + + expect(workflow).toContain('const allowedRoles = new Set(["admin", "maintain"]);'); + expect(workflow).toContain("const role = data.role_name ?? data.permission;"); + expect(workflow).not.toContain('new Set(["admin", "write"])'); + }); + it("lets npm-specific credential aliases override shared QA env", () => { expect( __testing.resolveCredentialSource({