Skip to content
dsh.fish
Bundle

dsh-jenkins

Jenkins CLI plugin (dual-face): multi-server config page, model tools, and a per-workspace Run-Jenkins-Job entry with environment tabs. Bilingual UI (zh/en), no hardcoded paths, publishable to npm / GitHub

Source
jsoncode
stars
2 stars
License
MIT
Updated
Updated 14 hours ago

Readme

# dsh-jenkins

<p align="center">
  <img src="assets/preview/1.png" alt="dsh-jenkins preview" width="800" />
</p>

**dsh-jenkins** is a Jenkins management plugin built on the DeepSeek Harness (DSH)
host, centralizing multiple servers and jobs for fast build publishing.

- **Multi-server / multi-job management** — configure and manage multiple Jenkins
  servers and jobs in one place
- **One-click publishing** — trigger parameterized builds, track
  queue → building → result in real time
- **Logs & operations** — view build logs, stop or cancel builds at any time
- **Bilingual UI** — follows the host interface language (中文 / English)

Available from the Settings page, a per-workspace entry, and model tools.

[中文文档](README.zh.md)

## Preview

Screenshots of the Settings page, workspace entry and run/history modals: see [preview.md](preview.md).

## Features

- **Settings → Jenkins Config** page (`settings.section`): add / edit / delete
  multiple servers (URL, username, Token), test connections, skip TLS verification.
  Only **Server URL** and **Token** are required (username defaults to `admin`).
- **Workspace entry** (`sidebar.footer.action`): a footer group with the **Jenkins
  logo button** (opens the Run Jenkins Job modal) and a **History button** (clock
  icon, publish history of the last 50 runs across all workspaces, filterable by
  workspace — defaults to All) appears when the current workspace root contains a
  `dsh-jenkins.{json,js,ts}` config file.
  The modal has **searchable dropdowns** for server / job, a parameter form
  pre-filled from the config, build triggering, and status polling (queued →
  building → result, with a 10-minute timeout). The server dropdown shows the
  **intersection of the servers referenced by the config and the servers configured
  in the plugin**; selecting a server auto-selects the configured job and echoes its
  parameters. The last submitted **server / job / parameters** are remembered per
  workspace and auto-echoed the next time the modal opens (browser `localStorage`).
  A missing or invalid config file is treated as "not configured" — no entry is shown.
- **Entry visibility**: the sidebar entry follows a **Show in menu** preference
  (default on), toggled from **Settings → Jenkins Config** or the top of the
  modal's **Config** tab. When off the entry renders nothing; the host settings
  page keeps an **Open Jenkins Config** button so the modal stays reachable
  (both places share one preference source and stay in sync).
- **Model tools** (docs/develop/basic/tool): `dsh_jenkins_build`, `dsh_jenkins_status`.
- **Config** (docs/develop/basic/config): Schemastery `Config` + a plugin data file
  `$DSH_HOME/dsh-jenkins.json` (server tokens encrypted with the machine-bound key
  `$DSH_HOME/dsh-jenkins.key`, cache in plaintext; on first run any legacy
  `dsh-jenkins` namespace in `settings.yaml` is migrated once and cleared).
- **Packaging** (docs/develop/basic/publish): `dsh.bundle` + `dsh.client`(web) manifests.

## Structure

```
├── src/host/*.ts       # Host half source: index.ts (entry), jenkins.ts (curl core), ops.ts (op dispatch), workspace-config.ts, types.ts
├── src/client/*.tsx    # Browser half source (React TSX components): Settings page, footer entry, run-job modal, history modal
├── lib/index.js        # Host half build artifact (tsdown, ESM), committed for git installs
├── lib/client.js       # Browser half build artifact (tsdown → __ModuleLoader__ factory), committed
├── lib/types/          # Type declarations (generated by tsc -b)
├── scripts/            # verify-client.mjs (host-seed simulation check)
├── tsdown.config.ts    # tsdown build config (node half + client bundle banner wrapper)
├── tsconfig.json       # solution: references tsconfig.host.json / tsconfig.client.json
├── cordis.patch.yml    # Bundle patch: plugin row referenced by package name (no paths)
├── package.json        # dsh.bundle + dsh.client(web) manifests + peerDependencies
├── README.md           # This file (English)
├── README.zh.md        # 中文文档
└── preview.md          # Screenshot preview (references assets/preview/*.png)
```

## Workspace config file (`dsh-jenkins.json` / `.js` / `.ts`)

Place it in the **workspace root**. It is an **array**; each element is one deploy
target (job + server + environments params). `.json` is parsed directly; `.js` / `.ts`
are evaluated with node (CJS `module.exports` or ESM `export default`):

```json
[
  {
    "job": "build-app",
    "server": "http://uat.example.com",
    "environments": { "BRANCH": "main", "DEPLOY": false }
  },
  {
    "job": "build-app",
    "server": "http://prod.example.com",
    "environments": { "BRANCH": "release-1.0", "DEPLOY": true }
  }
]
```

- Every element requires `job` (Jenkins job path, e.g. `build-app` or
  `folder/build-app`) and `server` (the server name / id / URL as configured in
  Settings → Jenkins).
- `environments` (optional): the parameter map for this target (booleans render as
  checkboxes, everything else as text fields).
- The modal's server dropdown shows the **intersection** of the servers referenced
  by the config and the servers configured in the plugin; selecting a server
  auto-selects the matching `job` (left empty when absent from the Jenkins job list,
  letting the user choose) and echoes its parameters. If the intersection is empty,
  the dropdown degrades to all servers with a hint. A missing or invalid config is
  treated as "not configured" — the entry is hidden.

## Installation

```sh
# Local development
dsh plugin --profile web add ./dsh-jenkins

# Published: npm / tarball / GitHub
dsh plugin --profile web add dsh-jenkins
dsh plugin --profile web add ./dsh-jenkins-0.1.4.tgz
dsh plugin --profile web add github:you/dsh-jenkins#<sha>

dsh --profile web --dump-config   # verify the layer
dsh --profile web                 # start (restart required for the host half to reload)
```

> **Local development dependencies**: the host loads `index.js` through native Node ESM, so
> `@deepseek-ai/schemastery`, `@deepseek-ai/dsh-tools` and `@deepseek-ai/dsh-settings` must be
> resolvable from the plugin directory (`node_modules` is gitignored). Either:
> 1. run `pnpm install` inside the plugin directory (these three are declared as
>    devDependencies); or
> 2. junction the host's flat fallback copies, e.g.:
>    ```powershell
>    New-Item -ItemType Directory "$PWD\node_modules\@deepseek-ai" -Force
>    foreach ($p in 'schemastery','dsh-tools','dsh-settings') {
>      New-Item -ItemType Junction "$PWD\node_modules\@deepseek-ai\$p" -Target "$env:DSH_HOME\profiles\node_modules\@deepseek-ai\$p"
>    }
>    ```

Static server defaults can also be set in the profile's `cordis.patch.yml`:

```yaml
- insert:
    - id: dsh-jenkins
      name: dsh-jenkins
      config:
        servers:
          - id: prod
            name: 生产环境
            baseUrl: https://jenkins.example.com
            username: admin
            token: <API Token or password>
            insecure: false
```

## Publish

The build toolchain is **tsc + tsdown** (same as `@lemcae/dsh-balance` and other
similar plugins — no vite): `tsc -b` type-checks and emits declarations, while
`tsdown` (Rolldown core) bundles the host half (`lib/index.js`, ESM) and the
browser half (`lib/client.js`, single-file CJS `__ModuleLoader__` factory with
auto banner wrapping). Dependency management uses **pnpm 10** (Node 26; the
`pnpm-lock.yaml` is committed and CI installs with `--frozen-lockfile`):

```sh
pnpm install     # install per pnpm-lock.yaml
pnpm run build   # clean lib → tsc -b (types + declarations) → tsdown (both halves)
pnpm run verify  # simulate the host module table to check lib/client.js (optional)
pnpm publish     # or pnpm pack / git push origin main (lib/ is committed; git installs need no build)
```

### Automated publishing (GitHub Actions)

Pushing a `v*` tag (`pnpm run release` bumps the patch version, rebuilds the
artifact, and tags it automatically) triggers
[`.github/workflows/publish.yml`](.github/workflows/publish.yml):

- **release job**: Setup Node 26 → `pnpm install --frozen-lockfile` →
  `pnpm run check` (tsc -b) → `pnpm run build` (tsc -b && tsdown) →
  `pnpm pack` → creates a GitHub Release (auto-generated changelog, tarball
  attached);
- **publish-npm job**: publishes to npm — requires the `NPM_TOKEN` repository
  secret (Settings → Secrets and variables → Actions); fails fast with a hint
  when it is missing.

## Development

Requirements: **Node ≥ 26 + pnpm 10** (the `packageManager` field in
`package.json` pins the pnpm version).

```sh
pnpm install           # devDependencies: typescript, tsdown, @types/react, @deepseek-ai/* type packages, etc.
pnpm run check         # whole-tree TypeScript type check (tsc -b)
pnpm run build         # rebuild both halves after editing source (tsc -b && tsdown)
pnpm run watch         # tsdown watch mode (rebuild on src/client changes)
pnpm run verify        # simulate the host seed table to check lib/client.js loads
```

- Host half lives in `src/host/`; browser half in `src/client/` (build entry
  `src/client/index.ts`, exporting `{ name, inject, apply }` directly);
- The `window.__ModuleLoader__.load` factory wrapper of `lib/client.js` is
  generated by tsdown's banner/intro/footer options (no hand-written wrap
  script);
- External dependencies in the artifact (`react`,
  `@deepseek-ai/dsh-client-ui-primitives`, ...) stay external and resolve from
  the host module table (seed) at runtime.

## Implementation notes

- Jenkins REST via `curl.exe` through the host `shell` service: Basic auth + CSRF crumb
  + `--data-binary @-` (form body over stdin, UTF-8 without BOM); `-D -` parses status
  and the `Location` header.
- Browser ↔ host transport: `ctx.remote.commands.execute(sessionId, '/dsh-jenkins <json>')`,
  host errors carry a `code` that the client localizes (fallback to the raw message).
- Peer dependencies (`@deepseek-ai/cordis`, `dsh-tools`, `schemastery`, `dsh-settings`,
  `dsh-commands`, `dsh-session`, `dsh-api-remotes`, client runtime/ui-slots/ui-settings/
  cordis-client-runner, `react`) are resolved by the host at install time.
- The official `deepseek-harness` project is **not modified**; all features use existing
  slots (`sidebar.footer.action`, `settings.section`, `shell.overlay`) and the command
  transport.
- **Style isolation**: every rule in the injected stylesheet is scoped to `.dshj-*`
  with one deliberate exception — `:where(div:has(> [data-slot="sidebar.footer.action"] > .dshj-footer-group)){flex-direction:column}`,
  which stacks the host footer container (the host lays it out as a flex **row**, so
  several plugin entries would squeeze onto one line). It can only match a container
  that already holds **this plugin's own entry**, and `:where()` drops its specificity
  to 0 so the host can always override it. Keyframe names are `dshj-`-prefixed and the
  style tag is marked `data-plugin-css="dsh-jenkins/settings.css"`; no other global
  selector, no `:root`/`body`/`*` rule, no body-style mutation.
- **Dialog palette**: the modals follow dsh-get-balance — a `rgba(0,0,0,.32)` scrim
  with `blur(12px) saturate(1.2)`, a `color-mix(bg-layer-1 78%)` glass panel with a
  `border-l2` hairline and 14px radius, `border-l1` header/footer dividers, solid
  `button-primary-fill` primary buttons and active tabs (translucent fills washed the
  host's monochrome `#0f1115` / `#f9fafb` primary into grey), `bg-base` inputs and
  dropdown panels, `bg-layer-2` cards, and `state-*` tokens for status colours.

Install

dsh plugin --profile web add github:jsoncode/dsh-jenkins

Profile: web

  • This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source