Changes from GitHub: a webhook of the repository, at
POST /hooks/github of the JSON API (Tayori.API). The
Automation guide adds it in GitHub.
GitHub signs each delivery with the webhook's secret
(x-hub-signature-256), and Tayori checks it against the body as it
came. The admin makes the secret, new_secret/0, and pastes it into
GitHub; Tayori keeps it encrypted, as the AI key (the :vault or the
:secret_key_base of Tayori.Config).
Two events make changes:
- A pull request merged into the default branch: one change, its
:refthe repository and the number ("owner/repo#123"). Its title is the pull request's, and its body the section of the pull request's description headed## Changelogor## Release notes, when there is one: never the whole description, which is often for the team. A pull request closed without a merge is nothing. - A release published: each item of its notes, as
Tayori.Sources.Changelogreads them. Items under### Addedhave that kind; items with no section have none. Notes with no item are one change, titled with the release's name. A prerelease is nothing yet.
A pull request's labels say what the change is, with the prefix of the
:github option of the :inbox config ("changelog:"):
changelog:skip- no change.changelog:internal- for the team only;changelog:public- for everyone.changelog:highlight- worth leading with.changelog:category:<key>- its category.changelog:segment:<segment>- the readers it is for (one label each).changelog:<kind>- its kind (changelog:added,changelog:fixed), and so its category (the:kindsof the:inboxconfig).
With only_labeled: true, a pull request with none of these labels is
nothing.
The raw body
The signature is of the body as GitHub sent it. An endpoint whose
Plug.Parsers reads JSON (a Phoenix app's does) reads the body before
the router; read_body/2 keeps a copy of it for the requests GitHub
signs, and only for them:
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
body_reader: {Tayori.GitHub, :read_body, []},
json_decoder: Phoenix.json_library()Without it, a delivery is answered with a 500, and the log says why.
Summary
Functions
The section of a pull request's description for its readers: the text
under a heading Changelog, Change log or Release notes, to the
next heading of its level, without HTML comments. Nil when there is no
such section, or it is empty.
The changes of an event of GitHub, x-github-event and its payload:
{:ok, changes} (none for a ping, or a pull request that is not
news), or :ignore for an event Tayori does not read. The options are
those of the :github option of the :inbox config, which they
replace.
Makes the webhook's secret, in place of any other, and returns it, to
paste into GitHub. Tayori keeps it encrypted, and never shows it again.
{:error, :no_secrets} without a :vault or a :secret_key_base.
A body reader for Plug.Parsers (body_reader: {Tayori.GitHub, :read_body, []}): it reads the body as Plug.Conn.read_body/2 does,
and keeps a copy of a body GitHub signed, for verify/2.
Removes the webhook's secret: GitHub's deliveries get a 404.
Whether there is a webhook secret.
Checks the signature of a body, the x-hub-signature-256 header
(sha256=<hex>), in constant time. Returns :ok, {:error, :no_secret}, or {:error, :invalid}.
Functions
The section of a pull request's description for its readers: the text
under a heading Changelog, Change log or Release notes, to the
next heading of its level, without HTML comments. Nil when there is no
such section, or it is empty.
iex> Tayori.GitHub.changelog_section("Fixes #12.\n\n## Changelog\n\nExports are faster.\n\n## Tests\n\nAdded.")
"Exports are faster."
@spec handle(String.t(), map(), keyword()) :: {:ok, [Tayori.Source.change()]} | :ignore
The changes of an event of GitHub, x-github-event and its payload:
{:ok, changes} (none for a ping, or a pull request that is not
news), or :ignore for an event Tayori does not read. The options are
those of the :github option of the :inbox config, which they
replace.
@spec new_secret() :: {:ok, String.t()} | {:error, :no_secrets}
Makes the webhook's secret, in place of any other, and returns it, to
paste into GitHub. Tayori keeps it encrypted, and never shows it again.
{:error, :no_secrets} without a :vault or a :secret_key_base.
@spec read_body(Plug.Conn.t(), keyword()) :: {:ok, binary(), Plug.Conn.t()} | {:more, binary(), Plug.Conn.t()} | {:error, term()}
A body reader for Plug.Parsers (body_reader: {Tayori.GitHub, :read_body, []}): it reads the body as Plug.Conn.read_body/2 does,
and keeps a copy of a body GitHub signed, for verify/2.
@spec remove_secret() :: :ok
Removes the webhook's secret: GitHub's deliveries get a 404.
@spec secret?() :: boolean()
Whether there is a webhook secret.
Checks the signature of a body, the x-hub-signature-256 header
(sha256=<hex>), in constant time. Returns :ok, {:error, :no_secret}, or {:error, :invalid}.