Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
51511e1
chore: add .gitignore (worktrees, Python artifacts)
river0525 Jul 21, 2026
8645e22
docs: add implementation plan for contest discussion automation
river0525 Jul 21, 2026
46e4ecc
feat: initialize uv project for contest-discussions automation
river0525 Jul 21, 2026
6ec76fb
feat: add contest_discussions package skeleton
river0525 Jul 21, 2026
95b6fc9
feat: add fixed repository/category IDs for NoviSteps-Writeups
river0525 Jul 21, 2026
8a56595
ci: add smoke test workflow for GITHUB_TOKEN discussions:write
river0525 Jul 21, 2026
f5c095a
fix: translate constants.py comment to English for consistency
river0525 Jul 21, 2026
62a255c
test: add AtCoder tasks page HTML fixture
river0525 Jul 21, 2026
87c6b38
feat: fetch task list from AtCoder tasks page
river0525 Jul 21, 2026
1e31a95
fix: harden fetch_tasks (byte-decode HTML, cross-check contest_id, do…
river0525 Jul 21, 2026
1f2a819
test: add AtCoder Problems contests.json fixture
river0525 Jul 21, 2026
8b1fd52
feat: find recently finished ABC contest ids
river0525 Jul 21, 2026
6f6a33a
fix: guard limit<=0 edge case, clarify docstrings for find_recently_f…
river0525 Jul 21, 2026
fb0672d
test: add failing tests for github_client discussion query/create
river0525 Jul 21, 2026
5991aef
feat: query existing discussion titles and create new discussions via…
river0525 Jul 21, 2026
446c28b
test: add failing tests for main orchestration/CLI
river0525 Jul 21, 2026
b7b79ff
feat: orchestrate contest discussion creation and add CLI entrypoint
river0525 Jul 21, 2026
9318580
fix: log tracebacks on failure, test CLI entrypoint and same-run dedup
river0525 Jul 21, 2026
a03758d
feat(ci): add production workflow to post contest discussions
river0525 Jul 21, 2026
50d20bd
chore(ci): remove throwaway smoke-test workflow
river0525 Jul 21, 2026
34b12ba
fix: add job timeout/concurrency guard, fail run on systemic fetch fa…
river0525 Jul 21, 2026
0903188
fix: address Copilot review findings
river0525 Jul 29, 2026
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
47 changes: 47 additions & 0 deletions .github/workflows/post_discussions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Post contest discussions

on:
schedule:
- cron: '50 13 * * 6'

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.

Problems API からコンテスト情報を取得する場合、コンテスト終了後10分では反映されていない可能性が高いかと思います

けんちょんさん、ウルズニャーさんとの議論から、日曜日に投稿できる状態であれば十分そうです
riverさんのお考えはいかがでしょうか?

- cron: '50 8 * * 0'
  timezone: 'Asia/Tokyo'

workflow_dispatch:
inputs:
contest_id:
description: 'Contest ID to backfill (e.g. abc467). Leave empty for automatic detection.'
required: false

permissions:
contents: read
discussions: write

concurrency:
group: post-discussions
cancel-in-progress: false

jobs:
post-discussions:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install uv
uses: astral-sh/setup-uv@v3

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.

astral-sh/setup-uv@c771a70 # v9.0.0

ようなコミットハッシュを使われてはいかがでしょうか?
(actions などの他の部分も同様です)

理由: セキュリティ対策(改ざん防止、サプライチェーン攻撃対策)のため


- name: Install dependencies
run: uv sync

- name: Post discussions
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CONTEST_ID: ${{ github.event.inputs.contest_id }}
run: |
if [ -n "$CONTEST_ID" ]; then
uv run python -m contest_discussions.main --contest-id "$CONTEST_ID"
else
uv run python -m contest_discussions.main
fi
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.worktrees/
.venv/
__pycache__/
*.pyc
172 changes: 172 additions & 0 deletions docs/dev-notes/2026-07-21/contest-discussion-automation/phase-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Phase 1: プロジェクト初期化 + GITHUB_TOKEN 権限検証

**目的:** uv プロジェクトの雛形を作り、「Actions 標準の `GITHUB_TOKEN` + `permissions: discussions: write` で Discussion 作成が可能」という設計上の前提を、実際にワークフローを1回動かして検証する。ここで検証できなければ、後続フェーズの認証方式を PAT に差し替える必要があるため、最初に確認する。

**注意:** このフェーズの最終ステップ (ワークフローの手動実行) は、公開リポジトリに実際の Discussion を作成する。**ユーザーの明示的な許可を得てから実行すること。** 無断でリモートに push したりワークフローを実行したりしない。

---

### Task 1: uv プロジェクトの初期化

**Files:**
- Create: `pyproject.toml`

- [ ] **Step 1: pyproject.toml を作成**

```toml
[project]
name = "contest-discussions"
version = "0.1.0"
description = "Automatically create GitHub Discussions for each ABC problem after the contest ends."
readme = "README.md"
requires-python = ">=3.13,<4.0"
dependencies = [
"requests>=2.32",
"selectolax>=0.4.6",
]

[dependency-groups]
dev = [
"pytest>=8.3",
"responses>=0.25",
]

[tool.pytest.ini_options]
pythonpath = ["src"]
```

- [ ] **Step 2: 依存関係をインストール**

Run: `uv sync`
Expected: `uv.lock` が生成され、`.venv/` が作成される

- [ ] **Step 3: Commit**

```bash
git add pyproject.toml uv.lock
git commit -m "chore: initialize uv project for contest discussion automation"
```

---

### Task 2: パッケージ雛形の作成

**Files:**
- Create: `src/contest_discussions/__init__.py`

- [ ] **Step 1: 空の `__init__.py` を作成**

```python
```

- [ ] **Step 2: Commit**

```bash
git add src/contest_discussions/__init__.py
git commit -m "chore: add contest_discussions package skeleton"
```

---

### Task 3: 定数モジュールの作成

**Files:**
- Create: `src/contest_discussions/constants.py`

- [ ] **Step 1: 定数を定義**

```python
"""Fixed IDs and text for the NoviSteps-Writeups repository.

See: https://github.com/AtCoder-NoviSteps/NoviSteps-Writeups
"""

# `gh api graphql -f query='query { repository(owner: "AtCoder-NoviSteps", name: "NoviSteps-Writeups") { id } }'`
REPOSITORY_ID = "R_kgDOTNgHyw"

# "General" discussion category.
# `gh api graphql -f query='query { repository(owner: "AtCoder-NoviSteps", name: "NoviSteps-Writeups") { discussionCategories(first: 10) { nodes { id name } } } }'`
GENERAL_CATEGORY_ID = "DIC_kwDOTNgHy84DAe9x"

DISCUSSION_BODY = "問題の感想や気づきを投稿・共有するスペースです"

# 週次 cron が1回失敗しても次回実行で埋め合わせられるよう、直近何件のABCを
# 重複チェック対象にするか。3件あれば3週分の取りこぼしまでカバーできる。
CONTESTS_TO_CHECK = 3
```

- [ ] **Step 2: Commit**

```bash
git add src/contest_discussions/constants.py
git commit -m "feat: add repository/category constants"
```

---

### Task 4: GITHUB_TOKEN の Discussion 書き込み権限を検証するワークフロー

**Files:**
- Create: `.github/workflows/smoke_test_discussion.yml`

- [ ] **Step 1: workflow_dispatch のみで動く検証用ワークフローを作成**

```yaml
name: Smoke test - GITHUB_TOKEN discussions:write

on:
workflow_dispatch:

permissions:
discussions: write

jobs:
smoke-test:
runs-on: ubuntu-latest
steps:
- name: Create a throwaway test discussion
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api graphql -f query='
mutation($repositoryId: ID!, $categoryId: ID!, $title: String!, $body: String!) {
createDiscussion(input: {
repositoryId: $repositoryId,
categoryId: $categoryId,
title: $title,
body: $body
}) {
discussion { id url }
}
}' \
-f repositoryId="R_kgDOTNgHyw" \
-f categoryId="DIC_kwDOTNgHy84DAe9x" \
-f title="[smoke test] GITHUB_TOKEN discussions:write check (safe to delete)" \
-f body="Created by CI to verify that the default GITHUB_TOKEN can create Discussions. Safe to delete."
```

- [ ] **Step 2: Commit**

```bash
git add .github/workflows/smoke_test_discussion.yml
git commit -m "ci: add smoke test workflow for GITHUB_TOKEN discussions permission"
```

- [ ] **Step 3 (ユーザー確認必須・手動): push してワークフローを実行**

ここは実際に公開リポジトリへ push し、ワークフローを実行して Discussion を1件作成する。**必ずユーザーに実行してよいか確認してから進めること。**

```bash
git push -u origin <branch-name>
gh workflow run smoke_test_discussion.yml
gh run watch
```

- [ ] **Step 4: 結果を確認**

- 成功した場合: NoviSteps-Writeups の Discussions に "[smoke test] ..." が作成されているので、`gh api graphql` の `deleteDiscussion` mutation か GitHub UI から削除する。以降のフェーズは `GITHUB_TOKEN` + `permissions: discussions: write` の方針で進める。
- 失敗した場合 (権限エラー等): `plan.md` の「却下した代替案」を更新し、Fine-grained PAT をリポジトリシークレット (`GITHUB_DISCUSSIONS_TOKEN` 等) として発行する方針に切り替える。Phase 4/6 の認証部分をその前提で書き直す。

- [ ] **Step 5: 検証用ワークフローを削除 (恒久ワークフローに置き換えるため)**

Phase 6 で正式なワークフローに差し替えるため、このスモークテスト用ファイルは Phase 6 で削除する (このフェーズでは残しておいてよい)。
Loading