<!-- task=m-jenkins-thin-artifact-ci plan=1 tag=REVIEW_JENKINS -->

# Jenkins Secret Bootstrap Follow-Up Plan

## 이 파일을 읽는 구현 에이전트에게

이 계획은 이전 코드리뷰 FAIL에서 나온 repo-owned Required issue 하나만 해결한다. 구현, 검증, 실제 출력 기록을 끝낸 뒤 `CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 채우고 active 파일을 유지한 채 리뷰 준비 상태로 멈춘다. 최종 판정, log rename, `complete.log` 작성, archive 이동은 code-review 스킬 전용이다.

구현 중 사용자만 결정할 수 있는 외부 환경 전제, secret 준비, scope 충돌이 발견되면 사용자에게 직접 묻지 말고 `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 정확한 결정, 근거, 실행한 명령과 출력, 재개 조건을 기록하고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 evidence 공백은 사용자 리뷰 요청이 아니라 검증 결과 또는 계획 대비 변경 사항에 기록한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/security-distribution/milestones/jenkins-thin-artifact-ci.md`
- Task ids:
  - `job-config`: `macbook-ultra` node, AppSok SCM, `main` branch, shell build step을 포함한 Jenkins job config를 만든다.
  - `script-call`: job build step이 `./scripts/build-certified-macos.sh`를 호출하고 secret 원문을 job config나 repository에 남기지 않는다.
  - `archive`: Jenkins archived artifact 설정으로 ZIP과 checksum을 보관한다.
  - `retention`: build log와 artifact 보관 정책을 최근 10개 build 기준에 맞춘다.
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Previous archived plan: `agent-task/m-jenkins-thin-artifact-ci/plan_cloud_G07_0.log`
- Previous archived review: `agent-task/m-jenkins-thin-artifact-ci/code_review_cloud_G07_0.log`
- Previous verdict: FAIL
- Required issue summary: `scripts/setup-appsok-ci-secrets.sh` creates only Apple/notary CI secret keys, while `scripts/upsert-jenkins-certified-job.sh` requires `jenkins_url`, `jenkins_username`, and `jenkins_api_token`; rerunning setup can omit or remove Jenkins credentials and make `--apply` fail.
- Affected files: `scripts/setup-appsok-ci-secrets.sh`, `test/certified_macos_scripts_test.dart`, `docs/macos-certified-build.md`.
- Verification evidence from prior review: local `git diff --check`, `bash -n scripts/upsert-jenkins-certified-job.sh`, targeted `flutter test test/certified_macos_scripts_test.dart`, remote Jenkins live `config.xml` marker check, remote `flutter test`, and remote `flutter analyze` passed after rerun.
- Allowed archive reread: read only the two archived log files above if exact prior context is needed; do not search `agent-task/archive/**`.

## 범위 결정 근거

- 이 follow-up은 Jenkins job config 자체를 다시 설계하지 않는다. 이미 live Jenkins config에서 node, branch, shell command, archive, retention marker가 확인됐다.
- 새 scope는 Jenkins credential bootstrap 재현성이다. setup script, 테스트, 문서만 수정한다.
- raw Jenkins endpoint, username, API token, Apple credential, keychain password는 tracked 파일, task log, 최종 응답에 기록하지 않는다.

## 구현 체크리스트

- [ ] [REVIEW_JENKINS-1] `scripts/setup-appsok-ci-secrets.sh`가 Jenkins URL, username, API token을 입력받아 `secrets/appsok.ci.sops.json` payload에 포함하게 하고 secret 원문을 출력하지 않는다.
- [ ] [REVIEW_JENKINS-2] 테스트와 문서를 갱신해 Jenkins secret bootstrap 경로, dry-run/apply 전제, credential rotation 시 보존되는 키를 검증한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_JENKINS-1] Jenkins Secret Bootstrap

#### 문제

`scripts/upsert-jenkins-certified-job.sh`는 `jenkins_url`, `jenkins_username`, `jenkins_api_token`을 필수 secret으로 읽는다. 그러나 `scripts/setup-appsok-ci-secrets.sh`의 payload 생성은 Apple/notary 키만 포함하므로 문서의 최초 설정 또는 credential rotation 흐름이 Jenkins apply에 필요한 키를 만들지 못한다.

#### 해결 방법

`scripts/setup-appsok-ci-secrets.sh`에 Jenkins URL, Jenkins username, Jenkins API token 입력을 추가하고, Python payload에 아래 키를 포함한다.

```json
{
  "jenkins_url": "<input>",
  "jenkins_username": "<input>",
  "jenkins_api_token": "<input>"
}
```

입력과 저장은 기존 SOPS payload 흐름을 재사용한다. raw secret 값은 stdout/stderr에 출력하지 않는다. Jenkins API token 입력도 plain prompt 패턴을 쓰되, 값 자체가 task log나 docs에 남지 않게 한다.

#### 테스트 작성

- `test/certified_macos_scripts_test.dart`
  - 기존 `secret setup stores repeatable encrypted CI inputs` 테스트에 `jenkins_url`, `jenkins_username`, `jenkins_api_token` payload key와 prompt/export 흐름 검증을 추가한다.
  - `jenkins job upsert keeps credentials out of generated config` 테스트가 setup script와 upsert script의 key contract를 함께 확인하게 한다.

#### 중간 검증

```bash
git diff --check -- scripts/setup-appsok-ci-secrets.sh scripts/upsert-jenkins-certified-job.sh test/certified_macos_scripts_test.dart docs/macos-certified-build.md
flutter test test/certified_macos_scripts_test.dart
```

Expected: whitespace check passes and targeted test passes.

### [REVIEW_JENKINS-2] Documentation And Verification

#### 문제

문서는 `secrets/appsok.ci.sops.json`에 Jenkins credential keys가 있다고 안내하지만, 최초 설정 섹션은 어떤 Jenkins 값을 입력해야 하는지 명시하지 않는다. 테스트도 setup script가 Jenkins keys를 생성한다는 계약을 고정하지 못한다.

#### 해결 방법

`docs/macos-certified-build.md`의 최초 설정 설명에 Jenkins URL, username, API token 입력이 준비된다는 내용을 추가한다. `--apply` 설명은 secret 원문을 노출하지 않고 Jenkins API 호출에만 사용한다는 점을 유지한다.

#### 테스트 작성

- 문서 자체의 snapshot 테스트는 추가하지 않는다.
- script contract는 `test/certified_macos_scripts_test.dart` 문자열/order 테스트로 고정한다.

#### 중간 검증

```bash
./scripts/upsert-jenkins-certified-job.sh --dry-run >/tmp/appsok-macos-certified-config.xml
for marker in "appsok-macos-certified" "macbook-ultra" "*/main" "AppSok-certified.zip.sha256" "<numToKeep>10</numToKeep>" "<artifactNumToKeep>10</artifactNumToKeep>"; do grep -F "$marker" /tmp/appsok-macos-certified-config.xml >/dev/null && printf "%s=present\n" "$marker"; done
```

Expected: required XML markers are present and no secret value is printed.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `scripts/setup-appsok-ci-secrets.sh` | REVIEW_JENKINS-1 |
| `test/certified_macos_scripts_test.dart` | REVIEW_JENKINS-1, REVIEW_JENKINS-2 |
| `docs/macos-certified-build.md` | REVIEW_JENKINS-2 |

## 최종 검증

```bash
git diff --check -- scripts/setup-appsok-ci-secrets.sh scripts/upsert-jenkins-certified-job.sh test/certified_macos_scripts_test.dart docs/macos-certified-build.md
flutter test test/certified_macos_scripts_test.dart
ssh -o BatchMode=yes -o ConnectTimeout=10 toki@toki-labs.com 'zsh -lc '\''cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && export PATH="$HOME/SDK/flutter/bin:/opt/homebrew/bin:/usr/local/bin:$PATH" && flutter analyze'\'''
ssh -o BatchMode=yes -o ConnectTimeout=10 toki@toki-labs.com 'zsh -lc '\''cd "$HOME/docker/services/code-server/data/volume/workspace/appsok" && export PATH="$HOME/SDK/flutter/bin:/opt/homebrew/bin:/usr/local/bin:$PATH" && flutter test'\'''
```

Expected: whitespace check passes, targeted test passes, remote analyze reports no issues, and remote full test passes.
