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 .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
* text=auto eol=lf

*.ino text eol=lf
*.hex -text
*.json text eol=lf
*.md text eol=lf
6 changes: 6 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/.github/ @owjs3901
/scripts/ @owjs3901
/schemas/ @owjs3901
/boards/*.hex @owjs3901
/boards/*.json @owjs3901
/registry.json @owjs3901
7 changes: 7 additions & 0 deletions .github/firmware-toolchain.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"schemaVersion": 1,
"arduinoCli": "1.5.1",
"platforms": {
"arduino:avr": "1.8.8"
}
}
37 changes: 37 additions & 0 deletions .github/workflows/guard-generated-files.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Generated File Guard

on:
pull_request_target:

permissions:
contents: read

jobs:
guard:
name: guard-generated-files
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Check out the trusted base revision
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.event.pull_request.base.sha }}
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.14"

- name: Inspect the pull request without checking out or executing its code
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
shell: bash
run: |
set -euo pipefail
git fetch --no-tags origin "refs/pull/${PR_NUMBER}/merge"
head_sha="$(git rev-parse FETCH_HEAD)"
python scripts/firmware.py --root . validate-pr \
--base "$BASE_SHA" \
--head "$head_sha"
86 changes: 86 additions & 0 deletions .github/workflows/publish-firmware.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Publish Firmware

on:
push:
branches:
- main
paths:
- ".github/firmware-toolchain.json"
- ".github/workflows/publish-firmware.yml"
- "boards/*.ino"
- "boards/*.png"
- "schemas/**"
- "scripts/firmware.py"
- "sources/boards/**"
workflow_dispatch:

permissions:
contents: write

concurrency:
group: hana-firmware-publisher
cancel-in-progress: false

jobs:
publish:
name: publish
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out protected main
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: main
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.14"
cache: pip
cache-dependency-path: requirements-dev.txt

- name: Set up Arduino CLI
uses: arduino/setup-arduino-cli@81d310742121c928ea9c8bbd407b4217b432ae02 # v2.0.0
with:
version: "1.5.1"

- name: Install validation dependencies
run: python -m pip install --requirement requirements-dev.txt

- name: Install locked Arduino platform
run: |
arduino-cli core update-index
arduino-cli core install arduino:avr@1.8.8

- name: Test publisher
run: python -m unittest -v tests.test_firmware

- name: Generate and publish verified firmware
shell: bash
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

for attempt in 1 2 3; do
git fetch origin main
git reset --hard origin/main
python scripts/firmware.py --root . generate --arduino-cli arduino-cli

if [[ -z "$(git status --porcelain -- boards registry.json)" ]]; then
echo "Published firmware is already current."
exit 0
fi

python scripts/firmware.py --root . verify --arduino-cli arduino-cli
git add -- boards registry.json
git commit -m "chore(firmware): publish compiled board artifacts"
if git push origin HEAD:main; then
exit 0
fi
echo "main advanced during publication; retrying (${attempt}/3)" >&2
done

echo "main kept advancing; firmware publication aborted" >&2
exit 1
51 changes: 51 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Validate

on:
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
validate:
name: validate
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.14"
cache: pip
cache-dependency-path: requirements-dev.txt

- name: Set up Arduino CLI
uses: arduino/setup-arduino-cli@81d310742121c928ea9c8bbd407b4217b432ae02 # v2.0.0
with:
version: "1.5.1"

- name: Install validation dependencies
run: python -m pip install --requirement requirements-dev.txt

- name: Install locked Arduino platform
run: |
arduino-cli core update-index
arduino-cli core install arduino:avr@1.8.8

- name: Run unit tests
run: python -m unittest -v tests.test_firmware

- name: Reject human changes to generated board data
if: github.event_name == 'pull_request'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: python scripts/firmware.py --root . validate-pr --base "$BASE_SHA"

- name: Compile every board twice
run: python scripts/firmware.py --root . compile --arduino-cli arduino-cli
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
__pycache__/
*.py[cod]

# Local planning artifacts must never be published in Hana Cloud.
docs/superpowers/
47 changes: 36 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
# Hana Cloud

Hana Cloud는 한번(HanBeon)이 사용하는 응용 프로그램 프로필과 외부 보드
자료를 배포하는 공개 데이터 저장소입니다. 실행 코드와 업로더 구현은 두지 않고,
검토 가능한 JSON·펌웨어·이미지만 관리합니다.
자료를 배포하는 공개 데이터 저장소입니다. 클라이언트 실행 코드와 업로더 구현은
두지 않고, 검토 가능한 JSON·펌웨어·이미지와 이를 검증·생성하는 CI만 관리합니다.

## 저장소 구조

```text
.gitattributes
registry.json
.github/
firmware-toolchain.json
workflows/
apps/
music-app.json
pdf-viewer.json
boards/
arduino-uno-r3.hex
arduino-uno-r3.json
arduino-uno-r3.ino
arduino-uno-r3.png
sources/boards/
arduino-uno-r3.json
contracts/
normalization-examples.json
schemas/
Expand Down Expand Up @@ -121,13 +127,22 @@ schemas/

```json
{
"schemaVersion": 1,
"schemaVersion": 2,
"id": "arduino.uno-r3",
"firmware": {
"path": "boards/arduino-uno-r3.ino",
"format": "arduino-sketch",
"path": "boards/arduino-uno-r3.hex",
"format": "intel-hex",
"size": 11211,
"sha256": "64자리 소문자 SHA-256",
"fqbn": "arduino:avr:uno",
"sha256": "64자리 소문자 SHA-256"
"source": {
"path": "boards/arduino-uno-r3.ino",
"sha256": "64자리 소문자 SHA-256"
},
"toolchain": {
"arduinoCli": "1.5.1",
"platform": "arduino:avr@1.8.8"
}
},
"wiring": [
{
Expand All @@ -151,6 +166,10 @@ schemas/

- `id`는 `registry.json` 보드 항목과 정확히 같아야 합니다.
- `firmware.path`와 선택적인 `image.path`는 같은 보드 basename을 사용합니다.
- `firmware`는 Arduino CLI가 필요 없는 일반 업로드용 Intel HEX입니다. bootloader를
포함한 HEX는 배포하지 않습니다.
- `firmware.source`와 `firmware.toolchain`은 소스와 바이너리의 대응을 감사하기 위한
provenance이며 CI가 계산합니다.
- 이미지를 제공하면 스크린 리더용 `alt` 설명이 반드시 있어야 합니다.
- 클라이언트는 manifest, 펌웨어, 이미지의 해시를 모두 확인한 뒤에만 로컬 경로를
업로더 인터페이스에 넘깁니다.
Expand Down Expand Up @@ -206,8 +225,8 @@ Uno의 VID/PID 목록과 USB 문자열은 Arduino의
검증하고 last-known-good 캐시에 원자적으로 저장합니다.
4. 새 프로필 적용 시 `미리보기 프로필 인식 완료 · 버튼 2개 추가`처럼 한 번만
알립니다. 다운로드 중이거나 실패한 상태를 300ms 폴링마다 반복 표시하지 않습니다.
5. 네트워크·검증 실패 시 마지막 정상 캐시를 유지하고, 캐시도 없으면 한번에 내장된
기본 프리셋 또는 기본 4칸으로 동작합니다.
5. 네트워크·검증 실패 시 마지막 정상 캐시를 유지하고, 캐시도 없으면 기본 4칸으로
동작합니다.

현재 HanBeon 펌웨어의 `HANBEON_UNO_V1` handshake는 펌웨어 설치가 끝난 보드와
런타임 연결을 맺는 기존 프로토콜로만 유지합니다. 최초 보드 식별이나 레지스트리
Expand Down Expand Up @@ -238,15 +257,21 @@ UI는 플랫폼 조건문을 갖지 않습니다.
단위 테스트를 추가합니다. 네트워크는 포함하지 않습니다.
3. **App profiles** — 인덱스 갱신, 프로필 검증·캐시·적용, 인식 완료 메시지를
추가합니다.
4. **Board catalog** — 보드 자료 검증·캐시, 배선 안내 UI, 다른 작업자가 구현하는
업로더에 넘길 안정적인 인터페이스를 추가합니다. 업로드 구현은 포함하지 않습니다.
4. **Board catalog** — 보드 자료 검증·캐시, 배선 안내 UI, 컴파일된 HEX를 다른
작업자가 구현하는 업로더에 넘길 안정적인 인터페이스를 추가합니다. 업로드 구현은
포함하지 않습니다.
5. **Desktop releases** — Changepacks가 만든 draft release에 Windows, macOS,
Linux Tauri 번들을 올리고 모든 빌드 성공 후 release를 공개합니다.

## 변경 규칙

- 데이터 변경은 PR로만 받습니다.
- `registry.json`과 대상 파일은 같은 PR에서 함께 갱신합니다.
- 보드 기여자는 `sources/boards/*.json`, 대응하는 `.ino`, 선택적인 `.png`만
수정합니다. `.hex`, 공개 board manifest, `registry.json`의 boards 항목은 사람이
수정할 수 없습니다.
- 보드 소스 PR이 병합되면 고정된 Arduino toolchain을 사용하는 GitHub Actions가
스케치를 두 번 clean build하고 동일한 일반 HEX만 후속 커밋으로 게시합니다.
- 앱 프로필 변경은 `registry.json`의 apps 항목과 대상 파일을 같은 PR에서 갱신합니다.
- 기존 `id`의 의미를 바꾸지 않습니다. 호환되지 않는 변경은 새 `id` 또는 새
`schemaVersion`을 사용합니다.
- 저작권이나 재배포 권한을 확인할 수 없는 펌웨어와 이미지는 추가하지 않습니다.
Loading
Loading