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
6 changes: 6 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Every change to this repository needs the approval of a member of @kaitencloud/engineering.
#
# The default-branch ruleset requires a review from a code owner. With this file, that means an
# approval from one of the team's members -- never from a GitHub App or a workflow's token,
# which cannot belong to a team, and never from the pull request's own author.
* @kaitencloud/engineering
21 changes: 21 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## What does this PR change?

<!-- Describe the change. -->

## Why?

<!-- Explain the motivation. -->

## Testing

<!-- Explain how this was tested. -->

## Checklist

- [ ] I have read `CONTRIBUTING.md`.
- [ ] Every commit in this PR includes a valid DCO `Signed-off-by` line.
- [ ] I have the right to submit all material in this PR.
- [ ] I have not included secrets or confidential data.
- [ ] I have updated tests where appropriate.
- [ ] I have updated documentation where appropriate.
- [ ] I have preserved required third-party licenses and attributions.
47 changes: 39 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
name: CI

# The default-branch ruleset requires three checks, named `lint`, `build` and `test`, as in
# Kaiten's SDK repositories. Each job below reports one of them.

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
lint-build-test:
name: Lint, Build & Test
lint:
name: lint
runs-on: ubuntu-latest

steps:
Expand All @@ -35,14 +38,42 @@ jobs:
- name: Verify module metadata
run: go mod tidy && git diff --exit-code -- go.mod go.sum

- name: Build
run: go build -v ./...

- name: Test
run: go test -race -shuffle=on -cover ./...

# govulncheck reports only vulnerabilities in code the build actually reaches, so a
# finding here is a real exposure in a shipped binary rather than a dependency-tree
# coincidence. Pinned to match the version `task vuln` runs locally.
- name: Vulnerability scan
run: go run golang.org/x/vuln/cmd/[email protected] ./...

build:
name: build
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.x"
cache-dependency-path: go.sum

- name: Build
run: go build -v ./...

test:
name: test
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.x"
cache-dependency-path: go.sum

- name: Test
run: go test -race -shuffle=on -cover ./...
113 changes: 113 additions & 0 deletions .github/workflows/dco.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: DCO

# Every commit of a pull request must be signed off by its author, as DCO.md and
# CONTRIBUTING.md ask: a `Signed-off-by: Name <email>` trailer naming the commit's author.
# Require the `DCO` check on main; this workflow is the source of truth.
#
# pull_request_target runs the workflow as it is on main, never as a pull request edits it,
# so a contributor cannot change the check that judges their own commits. It is safe because
# nothing here checks out or runs the pull request's code: the check below reads the commits'
# metadata from the API, and it needs no other file, so it can be copied to any repository.
on:
pull_request_target:
branches: ["main"]

permissions:
contents: read
pull-requests: read

jobs:
dco:
name: DCO
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Every commit is signed off by its author
shell: python
env:
GITHUB_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
"""Fail when a commit of the pull request is not signed off by its author (DCO 1.1)."""

import json
import os
import re
import sys
import urllib.request

SIGN_OFF = re.compile(
r"^Signed-off-by:[ \t]*(?P<name>.+?)[ \t]*<(?P<email>[^<>]+)>[ \t]*$", re.M
)
FIX = (
"Sign commits off with `git commit -s`. To fix this pull request:\n\n"
" git commit --amend --signoff --no-edit # the latest commit\n"
" git rebase --signoff origin/main # every commit of the branch\n"
" git push --force-with-lease\n\n"
"See CONTRIBUTING.md and DCO.md."
)


def problem(commit):
"""Why a commit, as the API lists a pull request's commits, fails; None if it passes.

A commit passes when one of its Signed-off-by trailers names its author, by name and
by email, ignoring case. A merge commit adds no work of its own, and a bot account
certifies nothing, so neither is checked.
"""
if len(commit.get("parents") or []) > 1:
return None
if (commit.get("author") or {}).get("type") == "Bot":
return None
author = commit["commit"]["author"]
signoffs = [(m["name"], m["email"]) for m in SIGN_OFF.finditer(commit["commit"]["message"])]
if any(
name.lower() == author["name"].lower() and email.lower() == author["email"].lower()
for name, email in signoffs
):
return None
expected = f"{author['name']} <{author['email']}>"
if not signoffs:
return f"no Signed-off-by; expected {expected}"
found = ", ".join(f"{name} <{email}>" for name, email in signoffs)
return f"signed off by {found}, not by its author: expected {expected}"


def commits():
"""Every commit of the pull request, page by page."""
api = os.environ.get("GITHUB_API_URL", "https://api.github.com")
repository, number = os.environ["GITHUB_REPOSITORY"], os.environ["PR_NUMBER"]
page = 1
while True:
request = urllib.request.Request(
f"{api}/repos/{repository}/pulls/{number}/commits?per_page=100&page={page}",
headers={
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {os.environ['GITHUB_TOKEN']}",
"X-GitHub-Api-Version": "2022-11-28",
},
)
with urllib.request.urlopen(request, timeout=30) as response:
batch = json.load(response)
yield from batch
if len(batch) < 100:
return
page += 1


def main():
listed = list(commits())
failed = [(commit, why) for commit in listed if (why := problem(commit)) is not None]
for commit, why in failed:
subject = commit["commit"]["message"].splitlines()[0]
print(f"::error title=DCO::{commit['sha']} {subject}: {why}")
if failed:
print(f"\n{len(failed)} of {len(listed)} commits are not signed off by their author.")
print(f"\n{FIX}")
return 1
print(f"All {len(listed)} commits are signed off by their author.")
return 0


if __name__ == "__main__":
sys.exit(main())
7 changes: 7 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ archives:
format_overrides:
- goos: windows
formats: [zip]
# Apache-2.0 asks every redistribution to carry LICENSE and NOTICE. GoReleaser includes
# LICENSE by default but not NOTICE; listing files replaces the defaults, so README is
# listed too.
files:
- LICENSE
- NOTICE
- README.md

checksum:
name_template: "checksums.txt"
Expand Down
66 changes: 66 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Contributing to the Kaiten CLI

Thank you for contributing to the Kaiten CLI.

This project is licensed under the Apache License, Version 2.0.

## Developer Certificate of Origin

Kaiten uses the Developer Certificate of Origin 1.1 (DCO) for contributions.

Every commit must include a `Signed-off-by` trailer matching the commit author.

Use:

```bash
git commit -s -m "Describe your change"
```

This produces:

```text
Signed-off-by: Jane Doe <[email protected]>
```

`git commit -s` is a **DCO sign-off**: it certifies the contribution under
[DCO.md](./DCO.md). `git commit -S`, with a capital S, is a **cryptographic
commit signature** made with a GPG or SSH key. They are not the same thing, and
the sign-off is what this project requires. There is nothing to install.

### Fixing a missing sign-off

A check named **DCO** verifies every commit of every pull request, and it is the
final word. If it fails, it names the commits to fix.

For the latest commit:

```bash
git commit --amend --signoff --no-edit
git push --force-with-lease
```

For several commits, sign off every commit of your branch at once:

```bash
git rebase --signoff origin/main
git push --force-with-lease
```

## Contribution rules

Please:

- keep pull requests focused;
- add or update tests when behavior changes;
- update documentation when relevant;
- do not include secrets, customer data, or confidential information;
- do not submit code or assets that you do not have the right to contribute;
- preserve required third-party license and attribution notices.

Accepted contributions are contributed under Apache-2.0.

## Security

Do not report unpatched vulnerabilities in public issues.

See [SECURITY.md](./SECURITY.md).
37 changes: 37 additions & 0 deletions DCO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or

(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or

(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.

(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
25 changes: 18 additions & 7 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Expand Down Expand Up @@ -167,16 +168,26 @@
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on behalf of Yourself, assuming sole
responsibility, not on behalf of any other Contributor, and only
if You agree to indemnify, defend, and hold each Contributor
harmless for any liability incurred by, or claims asserted against,
such Contributor by reason of your accepting any such warranty or
additional liability.
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

Copyright 2026 Kaiten
APPENDIX: How to apply the Apache License to your work.

To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
3 changes: 3 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Kaiten CLI

Copyright 2026 KAITEN INC
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,11 @@ stable, since scripts depend on them.

## License

Licensed under the [Apache License, Version 2.0](LICENSE).
The Kaiten CLI is open source and licensed under the
[Apache License, Version 2.0](./LICENSE).

By contributing, you agree to certify your contribution under the
[Developer Certificate of Origin 1.1](./DCO.md).

The Kaiten name and logos are not licensed under Apache-2.0. See the
[Kaiten trademark policy](https://github.com/kaitencloud/kaiten/blob/main/TRADEMARKS.md).
Loading
Loading