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
16 changes: 5 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,16 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Resolve server OpenAPI ref
- name: Read pinned server commit
id: server-openapi-ref
run: |
candidate="${SERVER_OPENAPI_REF:-${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}}"
if git ls-remote --exit-code --heads https://github.com/Life-USTC/server.git "$candidate" >/dev/null 2>&1; then
echo "ref=$candidate" >> "$GITHUB_OUTPUT"
else
echo "::warning::Server OpenAPI branch '$candidate' not found; falling back to main"
echo "ref=main" >> "$GITHUB_OUTPUT"
fi
run: echo "ref=$(./scripts/openapi-contract pinned-sha)" >> "$GITHUB_OUTPUT"

- name: Checkout server OpenAPI source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: Life-USTC/server
ref: ${{ steps.server-openapi-ref.outputs.ref }}
path: server
path: .openapi-server

- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
Expand All @@ -45,7 +38,8 @@ jobs:

- name: Check generated code is up-to-date
run: |
make sync-openapi generate OPENAPI_SOURCE=server/public/openapi.generated.json
make check-openapi-sync OPENAPI_SERVER_DIR=.openapi-server
make generate
git diff --exit-code

lint:
Expand Down
93 changes: 93 additions & 0 deletions .github/workflows/openapi-sync.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Sync OpenAPI contract

on:
schedule:
- cron: "23 4 * * *"
workflow_dispatch:
inputs:
server_sha:
description: Exact Life-USTC/server commit SHA (defaults to current main)
required: false
type: string
repository_dispatch:
types: [openapi-updated]

permissions:
contents: write
pull-requests: write

concurrency:
group: openapi-contract-sync
cancel-in-progress: false

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Resolve immutable server commit
id: server
env:
DISPATCH_SHA: ${{ github.event.client_payload.server_sha }}
INPUT_SHA: ${{ inputs.server_sha }}
run: |
explicit="${INPUT_SHA:-${DISPATCH_SHA:-}}"
if [[ -n "$explicit" ]]; then
if [[ ! "$explicit" =~ ^[0-9a-f]{40}$ ]]; then
echo "Explicit server_sha must be an exact 40-character lowercase commit SHA" >&2
exit 1
fi
sha="$explicit"
else
sha="$(git ls-remote https://github.com/Life-USTC/server.git refs/heads/main | awk '{print $1}')"
if [[ ! "$sha" =~ ^[0-9a-f]{40}$ ]]; then
echo "Could not resolve Life-USTC/server main to an immutable commit" >&2
exit 1
fi
fi
echo "sha=$sha" >> "$GITHUB_OUTPUT"

- name: Checkout exact server commit
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: Life-USTC/server
ref: ${{ steps.server.outputs.sha }}
path: .openapi-server

- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: go.mod

- name: Sync contract and generated client
run: |
make sync-openapi OPENAPI_SERVER_DIR=.openapi-server SERVER_COMMIT=${{ steps.server.outputs.sha }}
./scripts/openapi-contract verify
cmp -s .openapi-server/public/openapi.generated.json api/openapi.json
make generate

- name: Build, test, and vet
run: |
make build
make test
make vet

- name: golangci-lint
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
with:
version: v2.12.2

# Set OPENAPI_SYNC_TOKEN to a PAT or GitHub App token if automated PRs
# should trigger the repository's normal pull_request workflows.
- name: Open or update contract PR
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ secrets.OPENAPI_SYNC_TOKEN || github.token }}
branch: automation/openapi-contract
delete-branch: true
commit-message: "chore: sync OpenAPI contract"
title: "chore: sync OpenAPI contract"
body: |
Synchronizes the vendored OpenAPI contract and generated CLI client with an immutable `Life-USTC/server` commit.

The sync workflow completed build, race-enabled tests, vet, and lint before opening this PR. When it uses the default `GITHUB_TOKEN`, GitHub will not trigger a second pull-request CI run.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

# Build artifacts
dist/
/.openapi-server/

# IDE
.vscode/
Expand Down
5 changes: 5 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
version: 2

before:
hooks:
- make check-openapi-provenance generate
- git diff --exit-code

builds:
- main: ./cmd/life-ustc
binary: life-ustc
Expand Down
19 changes: 13 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
VERSION ?= dev
OPENAPI_SOURCE ?= ../server/public/openapi.generated.json
OPENAPI_SERVER_DIR ?= ../server
SERVER_COMMIT ?=
LDFLAGS := -ldflags "-X github.com/Life-USTC/CLI/internal/cmd/root.version=$(VERSION)"

.PHONY: build clean test lint install generate sync-openapi check-openapi-sync
.PHONY: build clean test lint vet install generate sync-openapi check-openapi-provenance check-openapi-sync

build:
build: check-openapi-provenance generate
go build $(LDFLAGS) -o life-ustc ./cmd/life-ustc

clean:
Expand All @@ -17,15 +18,21 @@ test:
lint:
golangci-lint run ./...

install:
vet:
go vet ./...

install: check-openapi-provenance generate
go install $(LDFLAGS) ./cmd/life-ustc

generate:
go tool oapi-codegen -config api/oapi-codegen.yaml api/openapi.json
go run ./internal/cmd/apicmd/genpaths

sync-openapi:
cp $(OPENAPI_SOURCE) api/openapi.json
./scripts/openapi-contract sync "$(OPENAPI_SERVER_DIR)" "$(SERVER_COMMIT)"

check-openapi-provenance:
./scripts/openapi-contract verify

check-openapi-sync:
cmp -s $(OPENAPI_SOURCE) api/openapi.json
./scripts/openapi-contract verify-source "$(OPENAPI_SERVER_DIR)"
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@
登录支持浏览器 OAuth(PKCE)与设备码;默认 server 为生产站点,也可用
`--server` / `LIFE_USTC_SERVER` 指向其它实例。

## OpenAPI 契约

CLI 从仓库内的 `api/openapi.json` 生成。`api/openapi.provenance.json` 记录
对应的 server 提交和 SHA-256;`make build` 会先验证来源并重新生成客户端,
再开始编译。

更新契约时,先检出确定的 `Life-USTC/server` 提交,再运行:

```sh
make sync-openapi OPENAPI_SERVER_DIR=/path/to/server SERVER_COMMIT=<40-character-sha>
make generate
```

CI 会核对固定的 server 提交,不再猜测同名分支。定时同步工作流也会在
server 契约发生变化时创建更新 PR。

## 安装

发布包:[GitHub Releases](https://github.com/Life-USTC/CLI/releases)。或:
Expand Down
32 changes: 24 additions & 8 deletions api/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1348,7 +1348,9 @@
"in": "query",
"name": "search",
"schema": {
"type": "string"
"type": "string",
"minLength": 2,
"maxLength": 200
}
},
{
Expand Down Expand Up @@ -1380,7 +1382,9 @@
"name": "page",
"schema": {
"type": "integer",
"format": "int64"
"format": "int64",
"minimum": 1,
"maximum": 100
}
},
{
Expand Down Expand Up @@ -1591,7 +1595,9 @@
"name": "page",
"schema": {
"type": "integer",
"format": "int64"
"format": "int64",
"minimum": 1,
"maximum": 100
}
},
{
Expand Down Expand Up @@ -1729,7 +1735,9 @@
"in": "query",
"name": "search",
"schema": {
"type": "string"
"type": "string",
"minLength": 2,
"maxLength": 200
}
},
{
Expand All @@ -1751,7 +1759,9 @@
"name": "page",
"schema": {
"type": "integer",
"format": "int64"
"format": "int64",
"minimum": 1,
"maximum": 100
}
},
{
Expand Down Expand Up @@ -1815,7 +1825,9 @@
"name": "page",
"schema": {
"type": "integer",
"format": "int64"
"format": "int64",
"minimum": 1,
"maximum": 100
}
},
{
Expand Down Expand Up @@ -1897,15 +1909,19 @@
"in": "query",
"name": "search",
"schema": {
"type": "string"
"type": "string",
"minLength": 2,
"maxLength": 200
}
},
{
"in": "query",
"name": "page",
"schema": {
"type": "integer",
"format": "int64"
"format": "int64",
"minimum": 1,
"maximum": 100
}
},
{
Expand Down
5 changes: 5 additions & 0 deletions api/openapi.provenance.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"repository": "Life-USTC/server",
"commit": "e32d01a31d2e3b79765448aed8665eb197b93977",
"sha256": "d56af5ae5da8dc89e5c3ef618bc39cd0ea9b64e93a8e0808e4e2ab6ffabd58f4"
}
Loading