-
Notifications
You must be signed in to change notification settings - Fork 4
Description
part of octokit/octokit.js#2127
Todos
- Create types for a set of endpoints for "github.com", "ghes-3.1", "ghes-3.0" #6
- Add common response headers to
github.com
which may be extended / overwritten byghes-3.1
/ghes-3.0
- Add a
version
parameter to theOctokit
constructor which can only be set togithub.com
by default. But when importing the types forghes-3.1
, the version may also be set toghes-3.1
. When importing the types forghes-3.0
, theversion
parameter can be set togithub.com
,ghes-3.1
, orghes-3.0
- Add
.test-d.ts
files to specify the desired behavior with the different types when setting the version on theOctokit
constructor and theoctokit.request
method. - Make
Octokit.ApiVersions
interface merging work with GHES additions in TypeScript #9 - Make the tests pass 😁
- Add the same
version
parameter tooctokit.request
parameters #12
Differences between platforms
There are several differences between the REST API at https://api.github.com/ ("dotcom", GItHub Enterprise Server ("GHES") instances and GitHub AE ("GHAE").
- Some endpoints will always only exist on one platform but not the other
- dotcom (e.g.
/orgs/{org}/settings/*
endpoints), - GHES (
/enterprises/*
endpoints).
- dotcom (e.g.
- New endpoints are always released on dotcom first, for example
GET /app/hook/deliveries
, which is not present in GHES 3.1 or older versions - Sometimes new parameters, response keys, or response headers are added to dotcom which do not propagate immediately to the other platforms
- GHES includes a
X-GitHub-Enterprise-Version
response header
However there are no cases when an existing REST API endpoint behaves differently in a breaking way, the changes are only additive, e.g. when dotcom graduated a preview header which was not yet graduated on a GHES version.
Also if a script needs to be compatible with multiple GHES versions, it is enough to be compatible with the lowest version. What works in GHES 3.0 will also always work in GHES 3.1, 3.2, etc. At least within the range of officially supported GHES versions.
What we have today
Today we have types for all the 600+ REST API endpoints in dotcom. We do track the OpenAPI specifications for the other platforms in https://github.com/octokit/openapi and https://github.com/octokit/openapi-types.ts, but we don't use the other types in any @octokit modules yet.
We do have a plugin for GHES versions: https://github.com/octokit/plugin-enterprise-server.js. When loading the plugins, only the REST API methods are loaded that exist in the respective GHES version, but the types are incorrect. For example developers get TypeScript IntelliSense for non-existing parameters and response keys, while e.g. the GHES-only response headers are missing. And there are no types at all for the .admin.*
endpoint methods, which makes the package unusable with TypeScript, as they build is failing. The TypeScript source code relies on REST API routes that do not exist in @octokit/types
, so TypeScript will complain when attempting to build it. We have a long-staning open issue for this problem: octokit/plugin-enterprise-server.js#84
What we want
By default, The Ocotkit
SDK should behave just as it does today:
const octokit = new Octokit({
auth: env.GITHUB_TOKEN
})
const response = octokit.request("GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries", {
owner,
repo,
hook_id
})
Now in order to write script targeting a minimal GHES version, an optional version
parameter can be set
const octokit = new Octokit({
auth: env.GITHUB_TOKEN,
version: "ghes-3.0"
})
const response = octokit.request("GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries", {
owner,
repo,
hook_id
})
The above code should not show a TypeScript error, explaining that the GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries
endpoint is not present in GHES 3.1 and below.
On the other side, the response.headers
type should now include { 'x-github-enterprise-version': string }
in this example
const octokit = new Octokit({
auth: env.GITHUB_TOKEN,
version: "ghes-3.0"
})
const response = octokit.request("GET /")
while it wouldn't be set if the version
option was set to "github.com"
or not set at all.
So instead of creating entirely separate types for all the different platforms/versions, I think we should have the types built upon each other. The github.com
version will include all the types we have today, then the ghes-3.1
endpoint types will extend the github.com
endpoints add add its differences, e.g. sent some of the routes to never
with a comment explaining that this endpoint is not availabil in GHES 3.1 and below, then ghes-3.0
will depend on ghes-3.1
and implement its differences, and so on.
Only the github.com
types should be loaded by default. If a developer wants to build against ghes-3.0
, they will have to install the respective types module and import it explicitly, e.g.
import { Octokit } from "octokit";
import "@octokit/types-endpoints-ghes-3.0";
const octokit = new Octokit({
auth: env.GITHUB_TOKEN,
version: "ghes-3.0"
})
const response = octokit.request("GET /")
That will import all necessary type differences between dotcom and GHES 3.1 as well as the differences between GHES 3.0 and 3.1
Compatibility versions
A common use case is to create code that is supposed to work against both dotcom and a minimal GHES version. For these cases we should create virtual compatibility versions, such as ghes-3.1-compatible
, which would include types from both dotcom and GHES 3.1, but will flag the types that that do not exist in both as optional. So e.g. this code should throw errors
const octokit = new Octokit({
auth: env.GITHUB_TOKEN,
version: "ghes-3.0"
})
// error: `GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries` does not exist on GHES 3.1`
octokit.request("GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries", {
owner,
repo,
hook_id
})
// error: `x-github-enterprise-version` header does not exist on `dotcom`
const response = await octokit.request("GET /")
console.log(response.headers["x-github-enterprise-version"])
To resolve the above error, the version must be permitted to be set explicitly on the request
octokit.request("GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries", {
owner,
repo,
hook_id,
request: { version: "github.com" },
})
const response = await octokit.request("GET /", { request: { version: "ghes-3.1" } })
console.log(response.headers["x-github-enterprise-version"])
Bonus: It would be nice if we could make the type code smart enough that e.g. checking for the presence of the x-github-enterprise-version
header would work to set the version
implicitly, so the code below wouldn't throw any errors.
const response = await octokit.request("GET /")
if ("x-github-enterprise-version" in response.headers) {
// version is now implicitly set to `ghes-3.1`
octokit.request("GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries", { owner, repo, hook_id })
}
Plugins
Plugin developers are a special use case, as they want their plugins to be compatible explicitly with specific version. I'm not sure what the best approach would be, maybe it would suffice to add automated tests which would set version
to all supported platform type versions, and then set a compatibility matrix as meta information on the plugin.
But either way, the least we need is to be able to explicitly set version on each request. That means that plugins might have their own dependency on endpoints type modules such as @octokit/types-endpoints-ghes-3.0
. Not ideal as depending on one plugin which depends on an old version of @octokit/types-endpoints-ghes-*
might result in a big amount of types-only dependencies, and even result loading outdated versions, so the app ends up having multiple versions of the types packages.