Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
6cf3053
refactor!: Delete project
nderjung Aug 7, 2026
681f44f
docs: Add README.md
nderjung Aug 7, 2026
d63cc90
build: Add uv packaging for unikraft-cloud
nderjung Aug 7, 2026
065aee7
build: Mark generated sources and set editor defaults
nderjung Aug 7, 2026
4bf46cd
build: Add Makefile driving openapi-gen
nderjung Aug 7, 2026
5b7bf80
ci: Add workflows for lint, tests, sync and publishing
nderjung Aug 7, 2026
00d5f53
feat(core): Add the error hierarchy
nderjung Aug 7, 2026
915e427
feat(core): Add the plumbing HTTP transport
nderjung Aug 7, 2026
de11c38
feat(core): Add metros and metro tagging
nderjung Aug 7, 2026
59e31d1
feat(core): Add resource references and envelope guards
nderjung Aug 7, 2026
4303a87
feat(core): Add multi-metro fan-out
nderjung Aug 7, 2026
ecb027b
feat(core): Add chainable resource handles
nderjung Aug 7, 2026
2ef0f96
feat(core): Add handle sets for names in several metros
nderjung Aug 7, 2026
5919766
feat(core): Add resource updates and the staged editor
nderjung Aug 7, 2026
c8cb311
feat(core): Add cursor pagination
nderjung Aug 7, 2026
bd8d427
feat(core): Encode request bodies through pydantic
nderjung Aug 7, 2026
d013661
fix(core): Let the serving metro win over the reported one
nderjung Aug 7, 2026
580821c
feat(templates): Render models as pydantic classes
nderjung Aug 8, 2026
c0481c0
build: Note the proto repository as a spec source
nderjung Aug 8, 2026
7ed2623
feat(templates): Render operations as plumbing clients
nderjung Aug 8, 2026
9c92f53
feat(api): Add the generated plumbing clients
nderjung Aug 8, 2026
3ed7f53
feat(api): Group the plumbing clients per surface
nderjung Aug 8, 2026
0b1d0ba
build: Mark the generated modules by their real suffix
nderjung Aug 8, 2026
a84c17e
feat(core): Add the session and metro discovery
nderjung Aug 8, 2026
3e1e6c5
feat(core): Add the resource base
nderjung Aug 8, 2026
c6acc59
fix(core): Do not warn about handles that were only counted
nderjung Aug 8, 2026
abe6233
feat(resources): Add the idiomatic instances client
nderjung Aug 8, 2026
2124936
feat: Add the top-level client
nderjung Aug 8, 2026
925bdaa
refactor(resources): Extract what every resource client repeats
nderjung Aug 8, 2026
7af4083
feat(resources): Add the idiomatic volumes client
nderjung Aug 8, 2026
3d1f4b5
feat(resources): Add the idiomatic service groups client
nderjung Aug 8, 2026
9ba345f
feat(resources): Add the idiomatic certificates client
nderjung Aug 8, 2026
28b7dc2
feat(resources): Add the idiomatic users client
nderjung Aug 8, 2026
fecd6e0
feat: Expose every resource on the client
nderjung Aug 8, 2026
6ec98ec
test: Add transport tests
nderjung Aug 8, 2026
a411585
test: Add tests for the core primitives
nderjung Aug 8, 2026
095025b
test: Add client and resource tests
nderjung Aug 8, 2026
70ce8eb
docs: Add runnable examples
nderjung Aug 8, 2026
1cc36fc
docs: Document the SDK
nderjung Aug 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 18 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

[Makefile]
indent_style = tab

[*.{json,yaml,yml,tmpl}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/*_gen.py linguist-generated=true
3 changes: 3 additions & 0 deletions .github/workflows/actionlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
actionlint:
runs-on: ubuntu-latest
Expand Down
122 changes: 61 additions & 61 deletions .github/workflows/check-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ name: check/pr

on:
pull_request:
types: [ opened, synchronize, reopened ]
branches: [ prod-staging ]
types: [opened, synchronize, reopened]
branches: [prod-staging]

permissions:
contents: read
Expand All @@ -12,70 +12,70 @@ jobs:
commits:
runs-on: ubuntu-latest
steps:
- name: format
if: always()
uses: taskmedia/action-conventional-commits@v1.1.20
with:
types: "build|ci|docs|feat|fix|perf|refactor|style|test|revert|chore|sync"
token: ${{ secrets.GH_PAT }}
- name: format
if: always()
uses: taskmedia/action-conventional-commits@v1.1.20
with:
types: "build|ci|docs|feat|fix|perf|refactor|style|test|revert|gomod|chore|sync"
token: ${{ secrets.GH_PAT }}

- name: length
if: ${{ github.actor != 'dependabot' && github.actor != 'dependabot[bot]' }}
uses: gsactions/commit-message-checker@v2
with:
pattern: '((^(?=(?:.|\n)*(?:^|\n)\[\d\]: .{69,}(?:$|\n)(?:.|\n)*)(?:.|\n)*$)|(^(?!(?:.|\n)*(?:^|\n).{74,}(?:$|\n)(?:.|\n)*)(?:.|\n)*$))'
flags: ''
error: 'The maximum line length of 74 characters is exceeded.'
excludeDescription: 'true'
excludeTitle: 'true'
checkAllCommitMessages: 'true'
accessToken: ${{ secrets.GH_PAT }}
- name: length
if: ${{ github.actor != 'dependabot' && github.actor != 'dependabot[bot]' }}
uses: gsactions/commit-message-checker@v2
with:
pattern: '((^(?=(?:.|\n)*(?:^|\n)\[\d\]: .{69,}(?:$|\n)(?:.|\n)*)(?:.|\n)*$)|(^(?!(?:.|\n)*(?:^|\n).{74,}(?:$|\n)(?:.|\n)*)(?:.|\n)*$))'
flags: ''
error: 'The maximum line length of 74 characters is exceeded.'
excludeDescription: 'true'
excludeTitle: 'true'
checkAllCommitMessages: 'true'
accessToken: ${{ secrets.GH_PAT }}

- name: signed-off-by
if: always()
uses: gsactions/commit-message-checker@v2
with:
pattern: '^Signed-off-by: .+ \<.+\@.+\..+\>$'
error: 'Signed-off-by line is missing.'
excludeDescription: 'true'
excludeTitle: 'true'
checkAllCommitMessages: 'true'
accessToken: ${{ secrets.GH_PAT }}
- name: signed-off-by
if: always()
uses: gsactions/commit-message-checker@v2
with:
pattern: '^Signed-off-by: .+ \<.+\@.+\..+\>$'
error: 'Signed-off-by line is missing.'
excludeDescription: 'true'
excludeTitle: 'true'
checkAllCommitMessages: 'true'
accessToken: ${{ secrets.GH_PAT }}

pr:
runs-on: ubuntu-latest
steps:
- name: title-format
if: always()
uses: gsactions/commit-message-checker@v2
with:
pattern: '^(build|ci|docs|feat|fix|perf|refactor|style|test|revert|chore|sync)(\([\w\-\_\d]+\))?!?: '
error: 'The PR title must follow the conventional commits format.'
excludeDescription: 'true'
excludeTitle: 'false'
checkAllCommitMessages: 'false'
accessToken: ${{ secrets.GH_PAT }}
- name: title-format
if: always()
uses: gsactions/commit-message-checker@v2
with:
pattern: '^(build|ci|docs|feat|fix|perf|refactor|style|test|revert|gomod|chore|sync)(\([\w\-\_\d]+\))?!?: '
error: 'The PR title must follow the conventional commits format.'
excludeDescription: 'true'
excludeTitle: 'false'
checkAllCommitMessages: 'false'
accessToken: ${{ secrets.GH_PAT }}

- name: title-length
if: ${{ github.actor != 'dependabot' && github.actor != 'dependabot[bot]' }}
uses: gsactions/commit-message-checker@v2
with:
pattern: '^(?!.{75,}).*'
flags: ''
error: 'The maximum line length of 75 characters is exceeded.'
excludeDescription: 'true'
excludeTitle: 'false'
checkAllCommitMessages: 'false'
accessToken: ${{ secrets.GH_PAT }}
- name: title-length
if: ${{ github.actor != 'dependabot' && github.actor != 'dependabot[bot]' }}
uses: gsactions/commit-message-checker@v2
with:
pattern: '^(?!.{75,}).*'
flags: ''
error: 'The maximum line length of 75 characters is exceeded.'
excludeDescription: 'true'
excludeTitle: 'false'
checkAllCommitMessages: 'false'
accessToken: ${{ secrets.GH_PAT }}

- name: description
if: ${{ github.actor != 'dependabot' && github.actor != 'dependabot[bot]' }}
uses: gsactions/commit-message-checker@v2
with:
pattern: '^\S+( \S+)*$'
error: 'The PR description must not be empty.'
flags: 'gm'
excludeDescription: 'false'
excludeTitle: 'true'
checkAllCommitMessages: 'false'
accessToken: ${{ secrets.GH_PAT }}
- name: description
if: ${{ github.actor != 'dependabot' && github.actor != 'dependabot[bot]' }}
uses: gsactions/commit-message-checker@v2
with:
pattern: '^\S+( \S+)*$'
error: 'The PR description must not be empty.'
flags: 'gm'
excludeDescription: 'false'
excludeTitle: 'true'
checkAllCommitMessages: 'false'
accessToken: ${{ secrets.GH_PAT }}
54 changes: 54 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: ci

on:
pull_request:
branches: [prod-staging, prod-stable]
push:
branches: [prod-staging]

concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true

- name: Sync dependencies
run: uv sync --locked

- name: Ruff lint
run: uv run ruff check .

- name: Ruff format check
run: uv run ruff format --check .

- name: Type-check
run: uv run mypy

test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true

- name: Run the test suite
run: uv run --python ${{ matrix.python-version }} pytest
73 changes: 73 additions & 0 deletions .github/workflows/publish-pypi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: publish-pypi

# Publish to PyPI via Trusted Publishing (OIDC) when a version tag is pushed,
# e.g. `git tag v0.1.0 && git push origin v0.1.0`. No API token is stored; the
# `pypi` GitHub Environment must be configured as a trusted publisher for the
# `unikraft-cloud` project on PyPI.

on:
push:
tags:
- "v*"

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true

- name: Check the tag and the package agree on the version
env:
TAG: ${{ github.ref_name }}
run: |
# The version is static in `pyproject.toml`, so a tag pushed without
# bumping it would build, and try to publish, the previous release.
version="$(uv version --short)"
if [ "${TAG#v}" != "${version}" ]; then
echo "::error::Tag ${TAG} does not match the packaged version ${version}." \
"Bump the version and retag."
exit 1
fi
exported="$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' src/unikraft_cloud/__init__.py)"
if [ "${exported}" != "${version}" ]; then
echo "::error::The package reports __version__ ${exported}, but it is built as ${version}."
exit 1
fi

- name: Build sdist and wheel
run: uv build

- name: Check metadata
run: uvx twine check dist/*

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/unikraft-cloud
permissions:
id-token: write
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
3 changes: 2 additions & 1 deletion .github/workflows/release-stable.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ name: release-stable

on:
push:
branches: [prod-staging]
branches:
- prod-staging

permissions:
deployments: write
Expand Down
13 changes: 11 additions & 2 deletions .github/workflows/sync.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,15 @@ jobs:
with:
ref: ${{ inputs.channel }}

- name: Install Go
uses: actions/setup-go@v5
with:
go-version: stable

- name: Install uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@v4
with:
enable-cache: true

- name: Generate
shell: bash
Expand Down Expand Up @@ -56,5 +63,7 @@ jobs:
branch: ${{ steps.id.outputs.name }}
committer: Unikraft Bot <monkey@unikraft.io>
author: Unikraft Bot <monkey@unikraft.io>
add-paths: controlplane,platform
# The whole generated package, so a module the spec drops is removed and
# a tag it adds is not left out of the pull request.
add-paths: src/unikraft_cloud/api
signoff: true
4 changes: 0 additions & 4 deletions .platform-config.yaml

This file was deleted.

Loading
Loading