runops 2.0.0__tar.gz → 2.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {runops-2.0.0 → runops-2.1.0}/PKG-INFO +5 -3
- {runops-2.0.0 → runops-2.1.0}/README.md +4 -2
- {runops-2.0.0 → runops-2.1.0}/SPEC.md +3 -2
- {runops-2.0.0 → runops-2.1.0}/docs/reference.md +2 -2
- {runops-2.0.0 → runops-2.1.0}/pyproject.toml +1 -1
- {runops-2.0.0 → runops-2.1.0}/src/runops/__init__.py +1 -1
- {runops-2.0.0 → runops-2.1.0}/src/runops/cli.py +26 -3
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/setup.py +33 -6
- {runops-2.0.0 → runops-2.1.0}/src/runops/skills/runops-reference/SKILL.md +1 -1
- {runops-2.0.0 → runops-2.1.0}/tests/conftest.py +6 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_setup_misc.py +62 -0
- {runops-2.0.0 → runops-2.1.0}/uv.lock +1 -1
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/check/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/improve-harness/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/agents/openai.yaml +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/examples/refactor-request.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/references/anti-patterns.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/references/api-compatibility.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/references/packaging-modernization.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/references/refactor-playbook.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/references/verification.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/scripts/api_surface_snapshot.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/scripts/import_smoke.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/scripts/inspect_python_package.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/scripts/refactor_quality_gate.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/release/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/simulator-adapter/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/spec-reviewer/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.agents/skills/sync-docs/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/agents/spec-reviewer.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/rules/architecture.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/rules/dev-workflow.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/rules/gotchas.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/settings.json +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/skills/check/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/skills/improve-harness/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/skills/python-package-refactor/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/skills/release/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/skills/simulator-adapter/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/skills/spec-reviewer/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.claude/skills/sync-docs/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.codex/README.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/.codex/agents/spec-reviewer.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/.codex/config.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/.codex/rules/runops.rules +0 -0
- {runops-2.0.0 → runops-2.1.0}/.dockerignore +0 -0
- {runops-2.0.0 → runops-2.1.0}/.gitattributes +0 -0
- {runops-2.0.0 → runops-2.1.0}/.github/assets/conversation-dark.svg +0 -0
- {runops-2.0.0 → runops-2.1.0}/.github/assets/conversation-light.svg +0 -0
- {runops-2.0.0 → runops-2.1.0}/.github/assets/hero-dark.svg +0 -0
- {runops-2.0.0 → runops-2.1.0}/.github/assets/hero-light.svg +0 -0
- {runops-2.0.0 → runops-2.1.0}/.github/assets/make_readme_art.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/.github/workflows/ci.yml +0 -0
- {runops-2.0.0 → runops-2.1.0}/.github/workflows/publish.yml +0 -0
- {runops-2.0.0 → runops-2.1.0}/.gitignore +0 -0
- {runops-2.0.0 → runops-2.1.0}/.harness/manifest.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/.pre-commit-config.yaml +0 -0
- {runops-2.0.0 → runops-2.1.0}/.vscode/settings.json +0 -0
- {runops-2.0.0 → runops-2.1.0}/AGENTS.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/CLAUDE.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/LICENSE +0 -0
- {runops-2.0.0 → runops-2.1.0}/docs/adapter.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/adapters/__init__.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/adapters/base.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/adapters/generic.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/adapters/registry.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/case.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/config.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/errors.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/harness.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/jobgen.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/launchers.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/legacy_adapters/__init__.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/legacy_adapters/beach.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/legacy_adapters/emses.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/manifest.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/__init__.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/common.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/create.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/listing.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/migrate.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/notes.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/skills.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/submit.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/sync.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/ops/tidy.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/project.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/runtime.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/skills/runops-check-status/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/skills/runops-create-run/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/skills/runops-maintain/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/skills/runops-tidy/SKILL.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/slurm.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/src/runops/util.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/__init__.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/.codex/rules/a.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/.runops/test-runs/T20260701-0001/receipt.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/.runops/work/beach-x-abc/bin/beach +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/AGENTS.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/CLAUDE.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/cases/beach/plane/beach.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/cases/beach/plane/case.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/cases/old/beach.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/cases/old/case.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/launchers.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/research/CURRENT.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runops.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runs/AGENTS.md +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runs/_archive/plane/R20260601-0001/manifest.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runs/plane/R20260701-0001--a/input/beach.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runs/plane/R20260701-0001--a/manifest.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runs/plane/R20260702-0001/manifest.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runs/scan/R20260701-0002/manifest.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runs/scan/survey.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/simulators.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/site.toml +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/golden/mpi_standard.sh +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/golden/openmp_rsc.sh +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_adapters.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_build.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_dev_harness.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_jobgen.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_legacy_adapters.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_lifecycle.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_manifest_config.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_migrate.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_scratch_tidy.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_skills.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_sweep.py +0 -0
- {runops-2.0.0 → runops-2.1.0}/tests/test_sync_evidence.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: runops
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Run ledger and Slurm job submission for HPC simulation projects
|
|
5
5
|
Project-URL: Homepage, https://github.com/Nkzono99/runops
|
|
6
6
|
Project-URL: Documentation, https://github.com/Nkzono99/runops/blob/main/docs/reference.md
|
|
@@ -60,8 +60,10 @@ mkdir lunar-sheath && cd lunar-sheath
|
|
|
60
60
|
runo init
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
`runo init` は計算機の種類と使う simulator を尋ねます。計算機はホスト名から推定するので、
|
|
64
|
+
京大スパコンのログインノードなら Enter で進めます。できた directory で Codex か Claude Code を
|
|
65
|
+
起動して、調べたいことを話しかけてください。設定を後から変えたいときも、エージェントに伝えれば
|
|
66
|
+
`runops.toml` を書き換えます。
|
|
65
67
|
|
|
66
68
|
## 頼めること
|
|
67
69
|
|
|
@@ -29,8 +29,10 @@ mkdir lunar-sheath && cd lunar-sheath
|
|
|
29
29
|
runo init
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
`runo init` は計算機の種類と使う simulator を尋ねます。計算機はホスト名から推定するので、
|
|
33
|
+
京大スパコンのログインノードなら Enter で進めます。できた directory で Codex か Claude Code を
|
|
34
|
+
起動して、調べたいことを話しかけてください。設定を後から変えたいときも、エージェントに伝えれば
|
|
35
|
+
`runops.toml` を書き換えます。
|
|
34
36
|
|
|
35
37
|
## 頼めること
|
|
36
38
|
|
|
@@ -169,7 +169,7 @@ schema = 2
|
|
|
169
169
|
name = "lunar-sheath"
|
|
170
170
|
|
|
171
171
|
[site]
|
|
172
|
-
name = "
|
|
172
|
+
name = "kudpc"
|
|
173
173
|
resource_style = "rsc" # "standard" | "rsc"
|
|
174
174
|
modules = ["intel/2023.2", "intelmpi/2023.2"]
|
|
175
175
|
stdout = "stdout.%J.log"
|
|
@@ -459,7 +459,7 @@ created ──submit──▶ submitted ──(slurm)──▶ running ──▶
|
|
|
459
459
|
|
|
460
460
|
| command | 効果 |
|
|
461
461
|
|---|---|
|
|
462
|
-
| `runo init [SIM...] [--site
|
|
462
|
+
| `runo init [SIM...] [--site kudpc\|standard] [--no-install] [-y]` | `runops.toml`、`cases/`、`runs/`、`LOG.md`、`AGENTS.md`、`.venv` を作る。SIM ごとに adapter package を `.venv` に install し、skill を展開する(§8)。端末から引数なしで実行すると site と simulator を尋ねる。`--site` が無ければ host 名から推定する(KUDPC のログインノードなら `kudpc`) |
|
|
463
463
|
| `runo doctor [--json]` | `runops.toml` の妥当性、adapter の import 可否、executable の存在、`sbatch` の有無、skill の鮮度を検査 |
|
|
464
464
|
| `runo update [--skills-only] [-y]` | adapter package を upgrade し、skill を再展開する |
|
|
465
465
|
| `runo build SIM [--force]` | `local_source` simulator を `.runops/build/` に build する(§4.1) |
|
|
@@ -911,3 +911,4 @@ v2 の `docs/` は次の 3 ファイルだけにする。
|
|
|
911
911
|
| submit 時の executable hash | attempt ごとに `exe_hash` を記録し、作成時と違えば warning | `runo update` 後の retry が作成時の hash のまま別 binary で走っていた |
|
|
912
912
|
| 同一入力の warning | `input_hash`(と `exe_hash`)が一致する既存 run を作成時に知らせる | §1.3 で観察した variation の重複を、ゲート無しで抑える |
|
|
913
913
|
| 整理と命名のしきい値 | `[defaults]` に `tidy_failed_days`、`tidy_archive_days`、`label_max_length` を追加。completed の目安は「`--older-than` の 3 倍」をやめて独立の値に | project ごとに計算期間や命名が違い、エージェントに毎回 option を渡させると揃わない |
|
|
914
|
+
| `runo init` の対話と site の推定 | 端末から引数なしで実行したときだけ site と simulator を尋ねる。site の既定はホスト名(`camphor*`、`laurel*`、`cinnamon*`、`gardenia*` なら `kudpc`)。プリセット名は `kudpc` とし、`camphor` も同じ意味で受け付ける | 人が打つのは `runo init` だけなので、ここで KUDPC 向けの設定を取り違えないようにする。エージェントや CI からの実行(端末でない、`-y`、引数あり)は従来どおり尋ねない |
|
|
@@ -14,7 +14,7 @@ schema = 2
|
|
|
14
14
|
name = "lunar-sheath"
|
|
15
15
|
|
|
16
16
|
[site]
|
|
17
|
-
name = "
|
|
17
|
+
name = "kudpc"
|
|
18
18
|
resource_style = "rsc" # "standard" | "rsc"
|
|
19
19
|
modules = ["intel/2023.2", "intelmpi/2023.2"]
|
|
20
20
|
stdout = "stdout.%J.log" # 省略時は work/%j.out
|
|
@@ -132,7 +132,7 @@ runops だけが書きます。手で編集しないでください。主な fie
|
|
|
132
132
|
|
|
133
133
|
| command | 動作 |
|
|
134
134
|
|---|---|
|
|
135
|
-
| `runo init [SIM...] [--site
|
|
135
|
+
| `runo init [SIM...] [--site kudpc\|standard] [--no-install] [-y]` | project を作る。`SIM` は `emses` や `name=package-spec`。端末から引数なしで実行すると site と simulator を尋ねる。`--site` の既定はホスト名から推定(KUDPC のログインノードなら `kudpc`、`camphor` も同義) |
|
|
136
136
|
| `runo doctor [--json]` | 設定、adapter の解決経路、executable、sbatch、skill の鮮度を点検 |
|
|
137
137
|
| `runo update [--skills-only] [-y]` | simulator package を更新し skill と AGENTS.md の管理 block を再生成 |
|
|
138
138
|
| `runo build SIM [--force]` | `local_source` の simulator を build |
|
|
@@ -107,21 +107,44 @@ def init(
|
|
|
107
107
|
sims: Optional[list[str]] = typer.Argument(
|
|
108
108
|
None, help="Simulators: NAME or NAME=package-spec (e.g. emses, beach, generic)."
|
|
109
109
|
),
|
|
110
|
-
site: str = typer.Option(
|
|
110
|
+
site: str = typer.Option(
|
|
111
|
+
"", "--site", help="Site preset: kudpc | standard (default: guessed from the host name)."
|
|
112
|
+
),
|
|
111
113
|
name: str = typer.Option("", "--name", help="Project name (default: directory name)."),
|
|
112
114
|
no_install: bool = typer.Option(
|
|
113
115
|
False, "--no-install", help="Do not create .venv or install packages."
|
|
114
116
|
),
|
|
115
117
|
yes: bool = typer.Option(False, "-y", "--yes"),
|
|
116
118
|
) -> None:
|
|
117
|
-
"""Create a runops project in the current directory.
|
|
118
|
-
|
|
119
|
+
"""Create a runops project in the current directory.
|
|
120
|
+
|
|
121
|
+
Run from a terminal without arguments, it asks for the site preset and the
|
|
122
|
+
simulators; the site default is guessed from the host name.
|
|
123
|
+
"""
|
|
124
|
+
from runops.ops.setup import SITE_PRESETS, detect_site, hostname, init_project
|
|
119
125
|
|
|
120
126
|
sims = sims or []
|
|
121
127
|
root = Path.cwd()
|
|
128
|
+
if not sims and not site and not yes and sys.stdin.isatty():
|
|
129
|
+
import click
|
|
130
|
+
|
|
131
|
+
guessed = detect_site()
|
|
132
|
+
site = typer.prompt(
|
|
133
|
+
f"Site preset (guessed from host {hostname()})",
|
|
134
|
+
default=guessed,
|
|
135
|
+
type=click.Choice(list(SITE_PRESETS)),
|
|
136
|
+
)
|
|
137
|
+
answer = typer.prompt(
|
|
138
|
+
"Simulators, comma-separated (emses, beach, generic; empty for none)",
|
|
139
|
+
default="",
|
|
140
|
+
show_default=False,
|
|
141
|
+
)
|
|
142
|
+
sims = [s.strip() for s in answer.split(",") if s.strip()]
|
|
122
143
|
if sims and not no_install and any(s.split("=")[0] != "generic" for s in sims):
|
|
123
144
|
_confirm(f"Create .venv in {root} and install simulator packages?", yes)
|
|
124
145
|
report = init_project(root, sims, site=site, name=name, install=not no_install)
|
|
146
|
+
if report.site_detected:
|
|
147
|
+
out.print(f"site: {report.site} (guessed from host {hostname()}; use --site to change)")
|
|
125
148
|
for item in report.created:
|
|
126
149
|
out.print(f"created {item}")
|
|
127
150
|
for spec in report.installed:
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import shutil
|
|
6
|
+
import socket
|
|
6
7
|
import subprocess
|
|
7
8
|
import sys
|
|
8
9
|
from dataclasses import dataclass, field
|
|
@@ -34,9 +35,9 @@ KNOWN_PACKAGES = {
|
|
|
34
35
|
KNOWN_EXECUTABLES = {"emses": "mpiemses3D", "beach": "beach"}
|
|
35
36
|
|
|
36
37
|
SITE_PRESETS: dict[str, dict[str, Any]] = {
|
|
37
|
-
"
|
|
38
|
+
"kudpc": {
|
|
38
39
|
"site": {
|
|
39
|
-
"name": "
|
|
40
|
+
"name": "kudpc",
|
|
40
41
|
"resource_style": "rsc",
|
|
41
42
|
"modules": ["intel/2023.2", "intelmpi/2023.2"],
|
|
42
43
|
"stdout": "stdout.%J.log",
|
|
@@ -52,6 +53,31 @@ SITE_PRESETS: dict[str, dict[str, Any]] = {
|
|
|
52
53
|
"launcher": {"kind": "srun", "use_slurm_ntasks": True},
|
|
53
54
|
},
|
|
54
55
|
}
|
|
56
|
+
SITE_ALIASES = {"camphor": "kudpc"}
|
|
57
|
+
# Login nodes of Kyoto University's supercomputer systems (A, B, C, G).
|
|
58
|
+
KUDPC_HOST_PREFIXES = ("camphor", "laurel", "cinnamon", "gardenia")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def hostname() -> str:
|
|
62
|
+
return socket.gethostname()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def detect_site(host: str | None = None) -> str:
|
|
66
|
+
"""Site preset suggested by the host name (``kudpc`` or ``standard``)."""
|
|
67
|
+
name = (hostname() if host is None else host).lower()
|
|
68
|
+
return "kudpc" if name.startswith(KUDPC_HOST_PREFIXES) else "standard"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def resolve_site(site: str) -> tuple[str, bool]:
|
|
72
|
+
"""Return (preset, detected) for a ``--site`` value; empty means detect."""
|
|
73
|
+
if not site:
|
|
74
|
+
return detect_site(), True
|
|
75
|
+
preset = SITE_ALIASES.get(site, site)
|
|
76
|
+
if preset not in SITE_PRESETS:
|
|
77
|
+
known = ", ".join([*SITE_PRESETS, *SITE_ALIASES])
|
|
78
|
+
raise UsageError(f"unknown site preset {site!r}; known: {known}")
|
|
79
|
+
return preset, False
|
|
80
|
+
|
|
55
81
|
|
|
56
82
|
GITIGNORE = """\
|
|
57
83
|
.venv/
|
|
@@ -81,6 +107,8 @@ Extend an existing file instead of creating a similar one.
|
|
|
81
107
|
@dataclass
|
|
82
108
|
class InitReport:
|
|
83
109
|
root: Path
|
|
110
|
+
site: str = ""
|
|
111
|
+
site_detected: bool = False
|
|
84
112
|
created: list[str] = field(default_factory=list)
|
|
85
113
|
installed: list[str] = field(default_factory=list)
|
|
86
114
|
warnings: list[str] = field(default_factory=list)
|
|
@@ -108,10 +136,9 @@ def init_project(
|
|
|
108
136
|
raise UsageError(f"{root / CONFIG_FILE} already exists")
|
|
109
137
|
if (root / "simulators.toml").exists():
|
|
110
138
|
raise UsageError(f"{root} is a runops v1 project; use `runo migrate-v1`")
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
report = InitReport(root)
|
|
139
|
+
site_key, detected = resolve_site(site)
|
|
140
|
+
preset = SITE_PRESETS[site_key]
|
|
141
|
+
report = InitReport(root, site=site_key, site_detected=detected)
|
|
115
142
|
simulators: dict[str, Any] = {}
|
|
116
143
|
for arg in sims:
|
|
117
144
|
sim_name, spec = parse_sim_arg(arg)
|
|
@@ -7,7 +7,7 @@ description: Full runops (runo) command reference, project layout and manifest f
|
|
|
7
7
|
|
|
8
8
|
| command | effect |
|
|
9
9
|
|---|---|
|
|
10
|
-
| `runo init [SIM...] [--site
|
|
10
|
+
| `runo init [SIM...] [--site kudpc\|standard] [--no-install] [-y]` | create runops.toml, cases/, runs/, LOG.md, notes/, AGENTS.md, skills; install simulator packages. Site defaults to a guess from the host name; asks interactively only in a terminal without arguments |
|
|
11
11
|
| `runo doctor [--json]` | check config, adapters, executables, sbatch, skill freshness |
|
|
12
12
|
| `runo update [--skills-only] [-y]` | upgrade simulator packages and redeploy skills / AGENTS.md block |
|
|
13
13
|
| `runo build SIM [--force]` | build a local_source simulator into .runops/build/<sim>-<commit> |
|
|
@@ -77,6 +77,12 @@ class FakeSlurm:
|
|
|
77
77
|
return str(self.next_id)
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
@pytest.fixture(autouse=True)
|
|
81
|
+
def fixed_hostname(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
82
|
+
"""Site detection must not depend on the machine running the tests."""
|
|
83
|
+
monkeypatch.setattr("runops.ops.setup.hostname", lambda: "testhost")
|
|
84
|
+
|
|
85
|
+
|
|
80
86
|
@pytest.fixture
|
|
81
87
|
def fake_slurm(monkeypatch: pytest.MonkeyPatch) -> FakeSlurm:
|
|
82
88
|
fake = FakeSlurm()
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import io
|
|
5
6
|
import json
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
8
11
|
from runops.ops.setup import init_project
|
|
9
12
|
from runops.project import Project
|
|
10
13
|
from runops.util import load_toml
|
|
@@ -81,3 +84,62 @@ def test_dest_must_stay_in_runs(project_dir: Path, cli) -> None: # type: ignore
|
|
|
81
84
|
def test_version(cli) -> None: # type: ignore[no-untyped-def]
|
|
82
85
|
res = cli("--version")
|
|
83
86
|
assert res.code == 0 and res.out.strip().startswith("2.")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_detect_site() -> None:
|
|
90
|
+
from runops.ops.setup import detect_site
|
|
91
|
+
|
|
92
|
+
for host in ("camphor31", "laurel32", "cinnamon1", "gardenia13", "Laurel5.kudpc.kyoto-u.ac.jp"):
|
|
93
|
+
assert detect_site(host) == "kudpc", host
|
|
94
|
+
for host in ("xb0092", "mylaptop", "login1"):
|
|
95
|
+
assert detect_site(host) == "standard", host
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_init_guesses_site_from_hostname(
|
|
99
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cli
|
|
100
|
+
) -> None: # type: ignore[no-untyped-def]
|
|
101
|
+
monkeypatch.setattr("runops.ops.setup.hostname", lambda: "laurel32")
|
|
102
|
+
monkeypatch.chdir(tmp_path)
|
|
103
|
+
res = cli("init", "--no-install")
|
|
104
|
+
assert res.code == 0, res.err
|
|
105
|
+
assert "site: kudpc (guessed from host laurel32" in res.out
|
|
106
|
+
config = load_toml(tmp_path / "runops.toml")
|
|
107
|
+
assert config["site"]["resource_style"] == "rsc"
|
|
108
|
+
assert config["site"]["name"] == "kudpc"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_init_explicit_site_wins(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cli) -> None: # type: ignore[no-untyped-def]
|
|
112
|
+
monkeypatch.setattr("runops.ops.setup.hostname", lambda: "laurel32")
|
|
113
|
+
monkeypatch.chdir(tmp_path)
|
|
114
|
+
res = cli("init", "--site", "standard", "--no-install")
|
|
115
|
+
assert "guessed" not in res.out
|
|
116
|
+
assert load_toml(tmp_path / "runops.toml")["site"]["resource_style"] == "standard"
|
|
117
|
+
assert cli("init", "--site", "nowhere", "--no-install").code == 2
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class _Tty(io.StringIO):
|
|
121
|
+
def isatty(self) -> bool:
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_init_interactive(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cli) -> None: # type: ignore[no-untyped-def]
|
|
126
|
+
monkeypatch.setattr("runops.ops.setup.hostname", lambda: "camphor31")
|
|
127
|
+
monkeypatch.chdir(tmp_path)
|
|
128
|
+
monkeypatch.setattr("sys.stdin", _Tty("\nemses, generic\n")) # Enter keeps the guess
|
|
129
|
+
res = cli("init", "--no-install")
|
|
130
|
+
assert res.code == 0, res.err
|
|
131
|
+
assert "guessed from host camphor31" in res.out
|
|
132
|
+
config = load_toml(tmp_path / "runops.toml")
|
|
133
|
+
assert config["site"]["resource_style"] == "rsc"
|
|
134
|
+
assert sorted(config["simulators"]) == ["emses", "generic"]
|
|
135
|
+
assert config["simulators"]["emses"]["modules"][0].startswith("hdf5/")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_init_not_interactive_with_yes(
|
|
139
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cli
|
|
140
|
+
) -> None: # type: ignore[no-untyped-def]
|
|
141
|
+
monkeypatch.chdir(tmp_path)
|
|
142
|
+
monkeypatch.setattr("sys.stdin", _Tty(""))
|
|
143
|
+
res = cli("init", "-y", "--no-install")
|
|
144
|
+
assert res.code == 0, res.err
|
|
145
|
+
assert load_toml(tmp_path / "runops.toml")["simulators"] == {}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/examples/refactor-request.md
RENAMED
|
File without changes
|
{runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/references/anti-patterns.md
RENAMED
|
File without changes
|
{runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/references/api-compatibility.md
RENAMED
|
File without changes
|
|
File without changes
|
{runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/references/refactor-playbook.md
RENAMED
|
File without changes
|
{runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/references/verification.md
RENAMED
|
File without changes
|
{runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/scripts/api_surface_snapshot.py
RENAMED
|
File without changes
|
{runops-2.0.0 → runops-2.1.0}/.agents/skills/python-package-refactor/scripts/import_smoke.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runs/plane/R20260701-0001--a/manifest.toml
RENAMED
|
File without changes
|
{runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runs/plane/R20260702-0001/manifest.toml
RENAMED
|
File without changes
|
{runops-2.0.0 → runops-2.1.0}/tests/fixtures/v1-project/runs/scan/R20260701-0002/manifest.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|