Skip to content

chore(deps): move the kpubdata pin to the 0.9 line - #1050

Merged
yeongseon merged 1 commit into
mainfrom
chore/kpubdata-0.9-pin
Oct 6, 2026
Merged

yeongseon merged 1 commit into
mainfrom
chore/kpubdata-0.9-pin

Conversation

@Eomdahyeon

@Eomdahyeon Eomdahyeon commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #1002
Refs #990, kpubdata-lab/kpubdata#780

kpubdata 0.9.0 이 PyPI 에 올라왔다(https://pypi.org/pypi/kpubdata/json → 0.9.0). kpubdata#812 가 정한 대로 핀을 옮긴다: kpubdata>=0.9.0,<0.10.

머지 전에 읽어 주세요 — 배포의 동작이 바뀝니다. 아래 "0.9.0 카탈로그가 말하는 약관" 절.

변경

  • pyproject.toml, uv.lock: kpubdata>=0.8.0,<0.9 → >=0.9.0,<0.10. 잠금 파일에서 바뀐 것은 kpubdata 항목뿐이다(uv lock --no-sources --upgrade-package kpubdata, --check 통과).
  • chore(deps): import find_spec, discover_specs and KPubDataConfig from kpubdata's public surface #1002: kpubdata 의 private 모듈에서 가져오던 import 7개를 public 이름으로 바꾸고 allowlist 항목을 지웠다.
    • find_spec (agent/monitor.py, agent/pipeline.py, cli.py), discover_specs (cli.py), KPubDataConfig (service/providers.py, verify/runner.py) — 이슈가 적은 6개.
    • SENSITIVE_PARAM_KEYS (logging_redaction.py) — 0.9.0 이 public 으로 내보낸다(kpubdata#782). 이슈에는 "남는 항목"으로 적혀 있었지만 이제 필요 없다.
    • 0.9.0 을 설치하면 게이트가 스스로 이것을 요구한다: 핀만 올린 상태에서 test_the_repository_passes_against_the_installed_kpubdata 가 실패했고, 메시지가 위 6개를 하나씩 짚었다.
    • 남은 allowlist 는 verify/runner.py 의 7개다: SpecExecutor, check_payload_error, extract_items, extract_total_count, ExampleSpec, SpecDefinition, HttpTransport.
  • tests/unit/test_verify_cli.py: find_spec 을 패치하는 대상을 kpubdata.core.spec → kpubdata 로. import 경로가 바뀌어 예전 대상은 더 이상 닿지 않는다(테스트 하나가 실패했다).
  • pyproject.toml 주석, CONTRIBUTING.md 의 핀 설명, cli.py 의 안내 문구를 0.9 기준으로.
  • CHANGELOG.

#990 에 대해

#1014 가 "env_keys 없는 kpubdata 로는 이 모드로 기동하지 않는다"를 넣었고, 0.8.0 에서는 그 거부가 항상 걸렸다. 0.9 라인에서는 Client 가 env_keys 를 받으므로 다중 사용자 모드가 기동한다. 0.8.0 이라 건너뛰던 테스트(test_require_own_credential.py 의 env_keys 실동작 테스트)가 이제 돌고 통과한다 — skip 수가 9 → 8 로 줄었다.

이슈의 세 조건이 이 핀으로 모두 충족되는지는 판단을 맡긴다. 그래서 Closes 가 아니라 Refs 다. client_keeps_environment_keys_out() 확인은 남겨 두었다 — 누군가 더 낮은 버전을 설치한 환경을 위한 것이다.

0.9.0 카탈로그가 말하는 약관 — 배포에서 달라지는 것

설치된 0.9.0 카탈로그를 읽으면(Client(...).datasets.list()): 약관 선언 없음 134, unknown 18, forbidden 1, allowed 3.

검증

  • pytest tests/unit (kpubdata 0.9.0 설치) → 4409 passed, 8 skipped (8분 12초).
  • scripts/check_kpubdata_imports.py → checked 246 files: no unlisted kpubdata private imports.
  • ruff check, ruff format --check, mypy src 통과.
  • 하지 않은 것: 실제 Builder 를 띄워 air_quality 빌드가 어떻게 답하는지 보지 않았다. e2e·데모 스크립트를 0.9.0 으로 돌리지 않았다. kpubdata 0.9.0 의 다른 breaking 변경(401 → AuthError, 건수 0, NODATA, 자격 증명 요청의 리다이렉트 미추적)이 Builder 의 실제 provider 호출 경로에 주는 영향은 단위 테스트가 통과한다는 것까지만 확인했다.
  • CI 의 kpubdata 0.8.0 잡은 이름이 워크플로에 박혀 있다 — 핀이 바뀌면 그 잡이 무엇을 설치하는지 확인이 필요하고, 워크플로 파일은 제가 고칠 수 없다.

🤖 Generated with Claude Code

kpubdata 0.9.0 is the first release with Client(env_keys=False). On 0.8.0 the
multi-user mode could not keep the operator's keys out of a request, so serve
refused to start there (#990); on the 0.9 line it starts.

Seven imports from kpubdata's private modules now use the public names the
release exports, and their allowlist entries are removed (#1002).

Closes #1002
Refs #990

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@yeongseon yeongseon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diff 를 읽었고 CLEAN 이라 승인합니다. 이 핀이 들어가야 다중 사용자(OIDC) 모드가 기동합니다.

핀과 public import 전환을 한 PR 로 묶은 것: 따로 올려 달라고 했지만 받아들입니다. 0.9.0 을 설치하면 check_kpubdata_imports.py 의 게이트가 allowlist 의 그 항목들을 스스로 거부한다는 설명이 맞고, 핀만 올린 PR 은 통과할 수 없었습니다.

확인한 것:

  • 잠금 파일에서 바뀐 것은 kpubdata 항목뿐입니다(0.8.0 → 0.9.0 의 sdist·wheel 해시와 specifier).
  • env_keys 실동작 테스트가 이제 돌고 skip 이 9 → 8 로 줄었다는 것. CI 의 kpubdata 0.9.0 잡과 kpubdata main ↔ Builder 잡이 모두 통과했습니다 — 본문이 걱정한 "kpubdata 0.8.0 잡 이름이 박혀 있다" 는 아니었습니다. 잡 이름이 지원 릴리스 목록에서 만들어집니다.
  • baseline 의 16개가 실제 0.9.0 카탈로그로 모두 unknown 이라는 것 — #1036 리뷰가 "핀을 올리는 PR 에서 봐야 한다" 고 남긴 확인입니다.

datago.air_quality 가 forbidden 이 되는 것 — 의도된 결과입니다. kpubdata 0.9.0 의 spec 이 그렇게 선언합니다(type: 공공누리_3유형, modification_allowed: false, redistribution: forbidden, kpubdata#720). #759 의 owner 결정(D6, 2026-09-30)이 air_quality 게시를 멈췄고 scheduled-air-quality.yml 은 비활성 상태입니다(마지막 실행 2026-09-29). 예약 게시 경로는 config 의 license_name: kogl-type-3 로 판정하므로 이 핀과 무관하게 이미 forbidden 입니다.

달라지는 것은 Builder 서비스 쪽입니다: air_quality 로 만든 빌드는 게시뿐 아니라 /query·/preview·다운로드도 막힙니다(#688 의 설계). 영향 범위를 확인했습니다 —

  • Builder 저장소의 specs·tests/e2e·docs·README.md 에서 air_quality 를 쓰는 것은 scripts/configs/air_quality.yaml 하나입니다.
  • Studio 의 실 Builder e2e(e2e/real-builder.spec.ts)는 replay 소스로 datago.air_station 을 씁니다. 0.9.0 에서 unknown 이라 읽기는 되고 공개 게시만 막힙니다. Studio 의 다른 e2e 일곱 파일이 air_quality 를 쓰지만 CI 에서는 mock 으로 돕니다.

남는 것: Studio 의 데모·문서·mock 이 air_quality 를 예제로 쓰는데 실제 Builder 에서는 그 데이터셋으로 아무것도 읽을 수 없게 됩니다. 예제를 다른 데이터셋으로 옮기는 이슈를 Studio 에 올려 주세요. 그리고 #990 은 누출 테스트(완료 조건 3)까지 보고 닫을지 정하면 됩니다 — Refs 로 둔 것이 맞습니다.

@yeongseon
yeongseon merged commit 0f4b02a into main Oct 6, 2026
26 checks passed
yeongseon pushed a commit that referenced this pull request Oct 6, 2026
…dential mode (#1051)

Closes #990

#1050 리뷰가 남긴 것: "#990 은 누출 테스트(완료 조건 3)까지 보고 닫을지 정하면 됩니다". 그 테스트다. 코드
변경은 없다.

## 이슈의 세 조건과 근거

1. **키 없는 빌드가 거부된다** —
`test_require_own_credential.py::test_build_is_refused_before_any_client_exists`
(#1014). 이 PR 의 두 번째 테스트도 실제 client 로 같은 것을 본다.
2. **`env_keys` 가 없는 kpubdata 로는 이 모드로 기동하지 않는다** —
`test_serve_refuses_to_start_when_it_cannot_keep_the_promise`,
`test_a_kpubdata_without_env_keys_is_refused_not_silently_used` (#1014).
3. **누출 테스트: 운영자 키 문자열이 요청·로그·매니페스트 어디에도 없다** — 이 PR. 지금까지의 테스트는 client
가 **무엇으로 만들어지는지**(`env_keys=False`, 키 조회가 `None`)를 봤고, 실제로 **무엇이 나가는지**는
보지 않았다. 0.8.0 에서는 볼 수 없었다.

## 테스트 (`tests/unit/test_operator_key_never_leaves.py`)

진짜 kpubdata `Client`(`cli._create_client`)와 진짜 `HttpTransport` 로
`BuilderService.build` 를 돌리고, 소켓(`httpx.Client.send`)만 바꿨다. 운영자 키는
kpubdata 가 읽는 이름 셋(`KPUBDATA_DATAGO_API_KEY`, `DATAGO_API_KEY`,
`KPUBDATA_API_KEY`) 모두에 canary 값으로 넣었다.

- **자기 키가 있는 요청자의 빌드:** 요청이 실제로 나갔고, 나간 요청마다 요청자의 키가 있고 운영자 키는 없다. 응답
본문, DEBUG 로그(본문과 레코드의 `extra`), run 디렉터리의 모든 파일(매니페스트·스냅샷·이벤트·산출물)에 운영자
키가 없다. 요청자의 키도 로그와 디스크에 없다.
- **키가 없는 요청자:** 403 `provider_credential_required` 이고 **요청이 하나도 나가지
않는다**(나가면 테스트가 그 자리에서 실패한다).
- **측정이 실제로 보는지:** 스위치를 끄면 같은 키 없는 빌드가 운영자 키로 나가고, 위 캡처에 그 키가 잡힌다. 이것이
없으면 "없다"는 단언들이 아무것도 안 나가도 통과한다.

데이터셋은 `datago.apt_trade` 를 썼다 — 0.9.0 에서 약관이 `allowed` 라 키 말고는 빌드를 막는 것이
없다(`air_quality` 는 이제 `forbidden`).

## 검증

- `pytest tests/unit/test_operator_key_never_leaves.py
tests/unit/test_require_own_credential.py` → `14 passed`.
- `ruff check`, `ruff format` 통과. `# type: ignore` 없음.
- kpubdata 가 `env_keys` 를 받지 않으면 파일 전체가 skip 된다(핀이 0.9 라인이라 CI 에서는 돈다).
- 전체 스위트는 CI 에 맡긴다. 실제 provider 호출은 아니다 — 응답은 표준 envelope 한 건을 흉내 낸 것이다.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Eomdahyeon <213566566+Eomdahyeon@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
yeongseon pushed a commit that referenced this pull request Oct 6, 2026
…1057)

## 무엇을

#992 의 남은 항목 가운데 "OIDC 설정으로 `serve` 기동 → `/healthz` 200" 스모크를 더합니다. 테스트
파일 하나와 CHANGELOG 뿐이고, 서비스 코드는 바꾸지 않았습니다.

## 어떻게

`tests/unit/test_oidc_start_smoke.py` 가 실제 `serve` 명령을 별도 프로세스로 띄웁니다.
환경은 다중 사용자 프로필입니다.

```
OIDC_ISSUER, OIDC_AUDIENCE, KPUBDATA_BUILDER_ADMIN_SUBJECTS=<issuer>|<sub>,
KPUBDATA_BUILDER_REQUIRE_OWN_PROVIDER_CREDENTIAL=true
```

`DEV_MODE`, `API_KEY`, `CREDENTIAL_MASTER_KEY` 는 테스트 실행 환경에 있더라도 빼고
띄웁니다.

- 프로세스가 뜨고 `GET /healthz` → 200 `{"status": "ok"}`
- 토큰 없이 `/providers`, `/builds`, `/version` → 401 `unauthorized`
- 검증할 수 없는 토큰 → 401 또는 503 `auth_unavailable` (통과시키지 않음)
- 부정: `OIDC_AUDIENCE` 가 없거나 `ADMIN_SUBJECTS` 가 없으면 "refusing to start" 로
종료

kpubdata 0.9.0 pin(#1050) 덕에 가능해졌습니다 — `REQUIRE_OWN_PROVIDER_CREDENTIAL`
이 켜져 있으면 `serve` 는 `env_keys` 를 모르는 kpubdata 에서 기동을 거부합니다(#990).

## 한계

- IdP 에 닿지 않습니다(`idp.invalid`). **서비스가 뜨고 닫혀 있다**는 것만 보이고, 로그인이 된다는 것은
보이지 않습니다. 수용 기준의 "실 Keycloak 토큰으로 `GET /providers` 200" 은 실제 realm 이 정해진
뒤의 일입니다.
- 컨테이너나 compose 를 띄우지 않습니다. 이미지·compose 의 변수 전달은
`test_prod_oidc_plumbing.py` 가 파일로 확인합니다.
- 손으로 띄웠을 때 본 것: 토큰이 없을 때의 401 문구가 OIDC 배포에서도 `api key not configured`
입니다. 동작은 맞고 문구만 어색합니다. 이 PR 에서는 건드리지 않았습니다.

## #992 수용 기준 대조

- [x] 배포 이미지에서 `import jwt` — #1020, #1033
- [x] OIDC 변수가 있는 프로필이 CI 에서 기동 스모크를 통과 — **이 PR** (프로세스 기동 기준. 컨테이너 기동은
아님)
- [ ] 실 Keycloak 토큰으로 `GET /providers` 200 — 배포 대상·realm 결정 필요

## 검증

- 새 파일 5건 통과 (약 5초), `ruff`, `mypy` 통과
- 전체 단위 스위트는 로컬에서 돌리지 않았습니다(테스트 추가만). CI 에 맡깁니다.

Refs #992 (닫지 않습니다)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Eomdahyeon <213566566+Eomdahyeon@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
yeongseon pushed a commit that referenced this pull request Oct 6, 2026
…1056)

## 무엇을

#802 — `POST /providers/{provider}/probe`. 요청의 `X-Provider-Key` 헤더에 실린
키로, 그 provider 의 데이터셋마다 kpubdata `PROBE_STATUSES`(available /
application_required / params_invalid …)와 소속 서비스(`service_id`)를 돌려줍니다.
계약 1.87.0 (additive).

kpubdata 0.9.0 이 `Client.probe` 와 `env_keys=False` 를 공개 API 로 내면서(#1050
에서 pin) 막혀 있던 것이 풀렸습니다.

## 동작

- **요청의 키만 씁니다.** 클라이언트를 `Client(provider_keys={provider: key},
cache=False, env_keys=False)` 로 만듭니다. 저장된 credential 이나 서버 환경변수의 키는 배포
모드와 무관하게 쓰지 않습니다. 헤더에 그 provider 의 키가 없으면 400 `provider_key_required`.
- **남기지 않습니다.** 요청이 끝나면 클라이언트를 닫고, 결과를 저장하지 않습니다. 응답에는 키에서 유도한 값이 없습니다.
- **데이터셋별.** kpubdata 가 spec 을 가진 데이터셋(오늘 datago 22, localdata 3)을 하나씩
호출합니다(15초, 재시도·캐시 없음 — kpubdata 의 probe 전송). body 의 `datasets` 로 좁힐 수
있습니다(최대 50).
- **시간 한도.** 45초가 지나면 새 호출을 시작하지 않고, 닿지 못한 데이터셋을 `not_probed` 에 적고
`complete: false` 로 답합니다. 관찰하지 않은 것에 상태를 붙이지 않습니다.

## 범위 대조 (이슈 체크리스트)

- [x] `POST /providers/{provider}/probe`, 키는 헤더로만, 요청이 끝나면 버림
- [x] 응답: 데이터셋별 `PROBE_STATUSES` 어휘 그대로 + `service_id`
- [ ] 결과를 계정에 저장 — **이 PR 은 서버에 저장하지 않습니다.** 응답에 키에서 유도한 값이 없어 호출자가 계정
기준으로 보관할 수는 있습니다. Builder 가 계정별로 보관해야 한다면(예: `GET /providers` 의
`last_test` 처럼) 후속으로 하겠습니다 — 결정이 필요합니다.
- [x] 키가 로그·에러·응답에 남지 않음 — 아래 테스트. 다만 #686 의 canary
gate(`test_canary_leak_gate.py`) 본체에 이 경로를 넣지는 않았고, 같은 방식(실제 kpubdata
`Client` + `httpx.MockTransport`)의 테스트를 이 파일에 뒀습니다.

## 테스트 (`tests/unit/test_provider_key_probe.py`, 19건)

- 헤더 없음 / 다른 provider 의 키만 있음 → 400, probe 를 열지 않음 (운영자 환경변수 키가 있어도)
- 실제 `Client`: 업스트림에 간 요청에 헤더의 키가 있고 운영자 키는 없음
- 실제 `Client`: 업스트림이 401/403/500 으로 요청 URL(키 포함)을 되돌려 줘도 응답과 DEBUG 로그에 키
없음
- 실제 `Client`: 다른 provider 로 연 probe 는 환경변수 키로 내려가지 않고 `auth_unknown`,
호출 0건
- probe 가 예외를 던지면 502 `probe unavailable` (예외 문구 미노출)
- body 검증 6가지, 모르는 provider 404, 시간 한도, spec 없는 데이터셋
- 200 응답을 계약 스키마로 검증

## 머지 순서

kpubdata-studio#764 를 먼저 머지해야 했고, **머지됐습니다**(스키마만 추가). 이 PR 은 200 응답 예시를
더하는데, Studio 의 drift 검사는 Builder `main` 의 모든 예시에 Studio 스키마를 요구합니다(스키마
없이 돌리면 1건 실패하는 것을 확인했습니다). 새 named error 예시는 넣지 않았습니다.

## 정해 주셨으면 하는 것

- 경로 이름(`/probe`, 이슈의 제안 그대로)과 응답 모양.
- 45초 한도·순차 호출. 병렬로 돌리면 빨라지지만 사용자 키로 provider 에 동시 호출을 내게 됩니다.
- `status` 를 계약에서 enum 으로 두지 않았습니다(kpubdata 가 상태를 더할 수 있어서).

## 검증

- `ruff check`, `ruff format`, `mypy src` 통과
- `scripts/check_contract_compat.py --base origin/main` → `compatible:
1.86.0 -> 1.87.0`
- 단위 스위트: `pytest tests/unit -x` 종료 코드 0, 실패 없음 (스킵 8건은 기존 것)

Closes #802

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Eomdahyeon <213566566+Eomdahyeon@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(deps): import find_spec, discover_specs and KPubDataConfig from kpubdata's public surface

2 participants