Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- `ci` config section: master switch, review engine (`static`/`model`), CI-driver auto-approve list, status-check name and blocking behavior, fail-on severity, poll interval, label/path filters, sensitive-path and test-existence rules, change-size caps, concurrency cap, comment posting, and report directory.
- `ci.run` write action joins the `allowedActions` default list.
- `requestTimeoutMs` config: hard per-request timeout that aborts the fetch when exceeded.
- Local-machine protection for the action scripts: `action-patch.mjs` and `action-post.mjs` refuse to run outside a GitHub Actions runner (no `RUNNER_TEMP` / `GITHUB_WORKSPACE`), and the new `scripts/local-test.mjs` simulates the composite action in a fully isolated sandbox — `DSH_HOME`, `DSH_PROFILE_DIR`, and the output directory are hardcoded under the system temp directory, overriding any inherited (machine-scope) `DSH_HOME`.

### Changed

Expand Down
2 changes: 2 additions & 0 deletions README.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ pnpm run check:readmes # cross-checks TOC anchors, tools, and config keys in a

Las pruebas simulan la API de GitHub, la CLI `gh` y git mediante runners inyectados — sin red, sin credenciales reales. `test/security.test.ts` verifica que la cadena del token nunca aparece en ninguna salida visible para el modelo o para el humano. `test/e2e.test.ts` contiene pruebas de humo optativas de la API real que se omiten automáticamente salvo que `DSH_GITHUB_E2E_TOKEN` esté definido (solo endpoints de solo lectura).

Para ejercitar la acción compuesta localmente, ejecuta `node scripts/local-test.mjs --owner-repo you/repo --pr 42` (consulta `--help` para todas las opciones). El simulador fija explícitamente `DSH_HOME`, `DSH_PROFILE_DIR`, `RUNNER_TEMP` y el directorio de salida dentro de un sandbox nuevo del directorio temporal del sistema para cada paso — tu dsh home real nunca se lee ni se escribe, incluso si existe un `DSH_HOME` de ámbito máquina — y reproduce los pasos install → prepare → run headless → post de `action.yml`. `action-patch.mjs` y `action-post.mjs` se niegan a ejecutarse fuera de un runner de GitHub Actions, de modo que la acción no puede escribir overlays de perfil ni informes en ubicaciones locales desconocidas.

## 🗂 Estructura del repositorio

```
Expand Down
2 changes: 2 additions & 0 deletions README.hi.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ pnpm run check:readmes # cross-checks TOC anchors, tools, and config keys in a

Tests injected runners के ज़रिए GitHub API, `gh` CLI, और git को mock करते हैं — कोई network नहीं, कोई real credentials नहीं। `test/security.test.ts` पुष्टि करता है कि token string किसी भी model- या human-visible output में कभी नहीं आता। `test/e2e.test.ts` में opt-in real-API smoke tests हैं जो `DSH_GITHUB_E2E_TOKEN` सेट न होने पर खुद को skip कर लेते हैं (केवल read-only endpoints)।

Composite action को locally आज़माने के लिए `node scripts/local-test.mjs --owner-repo you/repo --pr 42` चलाएँ (सभी options के लिए `--help` देखें)। Simulator हर spawned step के लिए `DSH_HOME`, `DSH_PROFILE_DIR`, `RUNNER_TEMP`, और output directory को system temp directory के एक नए sandbox में स्पष्ट रूप से fix करता है — आपका real dsh home कभी पढ़ा या लिखा नहीं जाता, चाहे machine-scope `DSH_HOME` मौजूद हो — और `action.yml` के install → prepare → headless run → post चरणों को replay करता है। `action-patch.mjs` और `action-post.mjs` GitHub Actions runner के बाहर चलने से मना कर देते हैं, इसलिए action किसी अज्ञात local location में profile overlay या report नहीं लिख सकता।

## 🗂 रिपॉज़िटरी संरचना

```
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ pnpm run check:readmes # cross-checks TOC anchors, tools, and config keys in a

Tests mock the GitHub API, the `gh` CLI, and git through injected runners — no network, no real credentials. `test/security.test.ts` asserts the token string never appears in any model- or human-visible output. `test/e2e.test.ts` contains opt-in real-API smoke tests that self-skip unless `DSH_GITHUB_E2E_TOKEN` is set (read-only endpoints only; the dedicated variable keeps the unit suite hermetic).

To exercise the composite action locally, run `node scripts/local-test.mjs --owner-repo you/repo --pr 42` (see `--help` for all options). The simulator hardcodes `DSH_HOME`, `DSH_PROFILE_DIR`, `RUNNER_TEMP`, and the output directory under a fresh system-temp sandbox for every spawned step — your real dsh home is never read or written, even when a machine-scope `DSH_HOME` exists — and replays the install → prepare → headless run → post steps of `action.yml`. `action-patch.mjs` and `action-post.mjs` refuse to run outside a GitHub Actions runner, so the action cannot write profile overlays or reports into unknown local locations.

## 🗂 Repository layout

```
Expand Down
2 changes: 2 additions & 0 deletions README.pt.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ pnpm run check:readmes # cross-checks TOC anchors, tools, and config keys in a

Os testes simulam a API do GitHub, a CLI `gh` e o git por meio de runners injetados — sem rede, sem credenciais reais. `test/security.test.ts` garante que a string do token nunca aparece em nenhuma saída visível ao modelo ou ao humano. `test/e2e.test.ts` contém testes de fumaça optativos da API real que se pulam automaticamente a menos que `DSH_GITHUB_E2E_TOKEN` esteja definido (apenas endpoints somente leitura).

Para exercitar a ação composta localmente, execute `node scripts/local-test.mjs --owner-repo you/repo --pr 42` (veja `--help` para todas as opções). O simulador fixa explicitamente `DSH_HOME`, `DSH_PROFILE_DIR`, `RUNNER_TEMP` e o diretório de saída em um sandbox novo do diretório temporário do sistema para cada etapa — seu dsh home real nunca é lido nem gravado, mesmo que exista um `DSH_HOME` de escopo de máquina — e reproduz os passos install → prepare → execução headless → post do `action.yml`. `action-patch.mjs` e `action-post.mjs` se recusam a executar fora de um runner do GitHub Actions, de modo que a ação não pode gravar overlays de perfil nem relatórios em locais desconhecidos.

## 🗂 Estrutura do repositório

```
Expand Down
2 changes: 2 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ pnpm run check:readmes # 交叉检查 5 个 README 的目录锚点、工具与

测试通过注入的 runner mock 掉 GitHub API、`gh` CLI 与 git——不联网、不用真实凭证。`test/security.test.ts` 断言 token 字符串不出现在任何模型或人类可见输出中。`test/e2e.test.ts` 是可选真实 API 冒烟测试:未设置 `DSH_GITHUB_E2E_TOKEN` 时自动跳过(只打只读端点;独立变量保证单测套件与环境隔离)。

要在本地演练 composite action,运行 `node scripts/local-test.mjs --owner-repo you/repo --pr 42`(全部选项见 `--help`)。模拟器为每个子进程显式写死 `DSH_HOME`、`DSH_PROFILE_DIR`、`RUNNER_TEMP` 与输出目录——全部位于全新的系统临时目录沙箱内,即使存在 Machine 级 `DSH_HOME` 也绝不读写你真实的 dsh home——并按 `action.yml` 的顺序回放 install → prepare → headless run → post 四步。`action-patch.mjs` 与 `action-post.mjs` 在 GitHub Actions runner 之外一律拒绝运行,因此 action 不会把 profile overlay 或报告写到未知的本地位置。

## 🗂 目录结构

```
Expand Down
14 changes: 14 additions & 0 deletions scripts/action-patch.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@
import { mkdirSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'

// Local-machine protection: the composite action resolves every path from the
// runner environment (RUNNER_TEMP for the output directory, GITHUB_WORKSPACE
// for the sandbox root). Outside a GitHub Actions runner those variables do
// not exist, and every fallback would silently write the profile overlay and
// task into the current working directory while a locally spawned `dsh`
// inherits the developer's real DSH_HOME. Refuse to run instead of guessing.
const runnerTemp = (process.env.RUNNER_TEMP ?? '').trim()
const workspace = (process.env.GITHUB_WORKSPACE ?? '').trim()
if (runnerTemp === '' || workspace === '') {
console.error('dsh-github: refusing to run outside a GitHub Actions runner — both RUNNER_TEMP and GITHUB_WORKSPACE must be set.')
console.error('dsh-github: to exercise the action locally, use `node scripts/local-test.mjs`; it pins DSH_HOME, DSH_PROFILE_DIR, and the output directory under the system temp directory.')
process.exit(1)
}

const env = (name) => process.env[name] ?? ''
const boolOf = (value, fallback) => value.trim() === '' ? fallback : value.trim() === 'true' || value.trim() === '1'
const listOf = (value) => value.split(',').map(item => item.trim()).filter(item => item.length > 0)
Expand Down
13 changes: 13 additions & 0 deletions scripts/action-post.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@
import { readFileSync, appendFileSync } from 'node:fs'
import { join } from 'node:path'

// Local-machine protection: the post step reads the CI result from the output
// directory and publishes step outputs through GITHUB_OUTPUT, both of which
// only exist on a GitHub Actions runner. Outside a runner the fallback would
// read an arbitrary `./dsh-github` directory next to the caller's cwd and
// report phantom verdicts. Refuse to run instead of guessing.
const runnerTemp = (process.env.RUNNER_TEMP ?? '').trim()
const workspace = (process.env.GITHUB_WORKSPACE ?? '').trim()
if (runnerTemp === '' || workspace === '') {
console.error('dsh-github: refusing to run outside a GitHub Actions runner — both RUNNER_TEMP and GITHUB_WORKSPACE must be set.')
console.error('dsh-github: to exercise the action locally, use `node scripts/local-test.mjs`; it pins DSH_HOME, DSH_PROFILE_DIR, and the output directory under the system temp directory.')
process.exit(1)
}

const outputDir = process.env.INPUT_OUTPUT_DIR?.trim() || join(process.env.RUNNER_TEMP ?? '.', 'dsh-github')
const resultPath = join(outputDir, 'dsh-github-ci-result.json')
const blocking = (process.env.INPUT_BLOCKING ?? 'true').trim() === 'true'
Expand Down
Loading
Loading