Initial commit
This commit is contained in:
commit
6f9ccf054b
19
.editorconfig
Normal file
19
.editorconfig
Normal file
|
@ -0,0 +1,19 @@
|
|||
# SPDX-License-Identifier: CC0-1.0
|
||||
# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = spaces
|
||||
insert_final_newline = true
|
||||
trim_trailing_whilespace = true
|
||||
|
||||
[README]
|
||||
indent_style = tabs
|
||||
|
||||
[*.rs]
|
||||
indent_size = 4
|
||||
indent_style = spaces
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
# SPDX-License-Identifier: CC0-1.0
|
||||
# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
/target
|
2377
Cargo.lock
generated
Normal file
2377
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
2
Cargo.lock.license
Normal file
2
Cargo.lock.license
Normal file
|
@ -0,0 +1,2 @@
|
|||
SPDX-License-Identifier: CC0-1.0
|
||||
SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
37
Cargo.toml
Normal file
37
Cargo.toml
Normal file
|
@ -0,0 +1,37 @@
|
|||
# SPDX-License-Identifier: CC0-1.0
|
||||
# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
[package]
|
||||
name = "pr-tracker"
|
||||
version = "0.1.0"
|
||||
authors = ["Alyssa Ross <hi@alyssa.is>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3.0-or-later WITH GPL-3.0-linking-exception"
|
||||
|
||||
[build-dependencies]
|
||||
pkg-config = "0.3.19"
|
||||
|
||||
[dependencies]
|
||||
http-types = "*"
|
||||
once_cell = "1.5"
|
||||
regex = "1.4"
|
||||
surf = "2.1"
|
||||
serde_json = "1.0"
|
||||
graphql_client = "0.8.0"
|
||||
serde = "1.0"
|
||||
askama = "0.10.5"
|
||||
structopt = "0.3.21"
|
||||
futures-util = "0.3.12"
|
||||
|
||||
[dependencies.async-std]
|
||||
version = "*" # Use whatever tide uses.
|
||||
features = ["attributes"]
|
||||
|
||||
[dependencies.tide]
|
||||
version = "0.16.0"
|
||||
default-features = false
|
||||
features = ["h1-server", "logger"]
|
||||
|
||||
[patch.crates-io]
|
||||
# https://github.com/djc/askama/pull/447
|
||||
askama = { git = "https://github.com/djc/askama", branch = "main" }
|
603
LICENSES/AGPL-3.0-or-later.txt
Normal file
603
LICENSES/AGPL-3.0-or-later.txt
Normal file
|
@ -0,0 +1,603 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||
document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for software
|
||||
and other kinds of works, specifically designed to ensure cooperation with
|
||||
the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed to take
|
||||
away your freedom to share and change the works. By contrast, our General
|
||||
Public Licenses are intended to guarantee your freedom to share and change
|
||||
all versions of a program--to make sure it remains free software for all its
|
||||
users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not price. Our
|
||||
General Public Licenses are designed to make sure that you have the freedom
|
||||
to distribute copies of free software (and charge for them if you wish), that
|
||||
you receive source code or can get it if you want it, that you can change
|
||||
the software or use pieces of it in new free programs, and that you know you
|
||||
can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights with two
|
||||
steps: (1) assert copyright on the software, and (2) offer you this License
|
||||
which gives you legal permission to copy, distribute and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that improvements made
|
||||
in alternate versions of the program, if they receive widespread use, become
|
||||
available for other developers to incorporate. Many developers of free software
|
||||
are heartened and encouraged by the resulting cooperation. However, in the
|
||||
case of software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and letting
|
||||
the public access it on a server without ever releasing its source code to
|
||||
the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to ensure that,
|
||||
in such cases, the modified source code becomes available to the community.
|
||||
It requires the operator of a network server to provide the source code of
|
||||
the modified version running there to the users of that server. Therefore,
|
||||
public use of a modified version, on a publicly accessible server, gives the
|
||||
public access to the source code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and published by
|
||||
Affero, was designed to accomplish similar goals. This is a different license,
|
||||
not a version of the Affero GPL, but Affero has released a new version of
|
||||
the Affero GPL which permits relicensing under this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification
|
||||
follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of works,
|
||||
such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this License.
|
||||
Each licensee is addressed as "you". "Licensees" and "recipients" may be
|
||||
individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work in
|
||||
a fashion requiring copyright permission, other than the making of an exact
|
||||
copy. The resulting work is called a "modified version" of the earlier work
|
||||
or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based on the
|
||||
Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without permission,
|
||||
would make you directly or secondarily liable for infringement under applicable
|
||||
copyright law, except executing it on a computer or modifying a private copy.
|
||||
Propagation includes copying, distribution (with or without modification),
|
||||
making available to the public, and in some countries other activities as
|
||||
well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other parties
|
||||
to make or receive copies. Mere interaction with a user through a computer
|
||||
network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices" to the
|
||||
extent that it includes a convenient and prominently visible feature that
|
||||
(1) displays an appropriate copyright notice, and (2) tells the user that
|
||||
there is no warranty for the work (except to the extent that warranties are
|
||||
provided), that licensees may convey the work under this License, and how
|
||||
to view a copy of this License. If the interface presents a list of user
|
||||
commands or options, such as a menu, a prominent item in the list meets this
|
||||
criterion.
|
||||
|
||||
1. Source Code.
|
||||
The "source code" for a work means the preferred form of the work for making
|
||||
modifications to it. "Object code" means any non-source form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official standard
|
||||
defined by a recognized standards body, or, in the case of interfaces specified
|
||||
for a particular programming language, one that is widely used among developers
|
||||
working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other than
|
||||
the work as a whole, that (a) is included in the normal form of packaging
|
||||
a Major Component, but which is not part of that Major Component, and (b)
|
||||
serves only to enable use of the work with that Major Component, or to implement
|
||||
a Standard Interface for which an implementation is available to the public
|
||||
in source code form. A "Major Component", in this context, means a major
|
||||
essential component (kernel, window system, and so on) of the specific operating
|
||||
system (if any) on which the executable work runs, or a compiler used to produce
|
||||
the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all the source
|
||||
code needed to generate, install, and (for an executable work) run the object
|
||||
code and to modify the work, including scripts to control those activities.
|
||||
However, it does not include the work's System Libraries, or general-purpose
|
||||
tools or generally available free programs which are used unmodified in performing
|
||||
those activities but which are not part of the work. For example, Corresponding
|
||||
Source includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically linked
|
||||
subprograms that the work is specifically designed to require, such as by
|
||||
intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users can regenerate
|
||||
automatically from other parts of the Corresponding Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
All rights granted under this License are granted for the term of copyright
|
||||
on the Program, and are irrevocable provided the stated conditions are met.
|
||||
This License explicitly affirms your unlimited permission to run the unmodified
|
||||
Program. The output from running a covered work is covered by this License
|
||||
only if the output, given its content, constitutes a covered work. This License
|
||||
acknowledges your rights of fair use or other equivalent, as provided by copyright
|
||||
law.
|
||||
|
||||
You may make, run and propagate covered works that you do not convey, without
|
||||
conditions so long as your license otherwise remains in force. You may convey
|
||||
covered works to others for the sole purpose of having them make modifications
|
||||
exclusively for you, or provide you with facilities for running those works,
|
||||
provided that you comply with the terms of this License in conveying all material
|
||||
for which you do not control copyright. Those thus making or running the
|
||||
covered works for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of your copyrighted
|
||||
material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the conditions
|
||||
stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
No covered work shall be deemed part of an effective technological measure
|
||||
under any applicable law fulfilling obligations under article 11 of the WIPO
|
||||
copyright treaty adopted on 20 December 1996, or similar laws prohibiting
|
||||
or restricting circumvention of such measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid circumvention
|
||||
of technological measures to the extent such circumvention is effected by
|
||||
exercising rights under this License with respect to the covered work, and
|
||||
you disclaim any intention to limit operation or modification of the work
|
||||
as a means of enforcing, against the work's users, your or third parties'
|
||||
legal rights to forbid circumvention of technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
You may convey verbatim copies of the Program's source code as you receive
|
||||
it, in any medium, provided that you conspicuously and appropriately publish
|
||||
on each copy an appropriate copyright notice; keep intact all notices stating
|
||||
that this License and any non-permissive terms added in accord with section
|
||||
7 apply to the code; keep intact all notices of the absence of any warranty;
|
||||
and give all recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey, and you
|
||||
may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
You may convey a work based on the Program, or the modifications to produce
|
||||
it from the Program, in the form of source code under the terms of section
|
||||
4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified it, and
|
||||
giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is released under
|
||||
this License and any conditions added under section 7. This requirement modifies
|
||||
the requirement in section 4 to "keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this License to anyone
|
||||
who comes into possession of a copy. This License will therefore apply, along
|
||||
with any applicable section 7 additional terms, to the whole of the work,
|
||||
and all its parts, regardless of how they are packaged. This License gives
|
||||
no permission to license the work in any other way, but it does not invalidate
|
||||
such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display Appropriate
|
||||
Legal Notices; however, if the Program has interactive interfaces that do
|
||||
not display Appropriate Legal Notices, your work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent works,
|
||||
which are not by their nature extensions of the covered work, and which are
|
||||
not combined with it such as to form a larger program, in or on a volume of
|
||||
a storage or distribution medium, is called an "aggregate" if the compilation
|
||||
and its resulting copyright are not used to limit the access or legal rights
|
||||
of the compilation's users beyond what the individual works permit. Inclusion
|
||||
of a covered work in an aggregate does not cause this License to apply to
|
||||
the other parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
You may convey a covered work in object code form under the terms of sections
|
||||
4 and 5, provided that you also convey the machine-readable Corresponding
|
||||
Source under the terms of this License, in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product (including
|
||||
a physical distribution medium), accompanied by the Corresponding Source fixed
|
||||
on a durable physical medium customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product (including
|
||||
a physical distribution medium), accompanied by a written offer, valid for
|
||||
at least three years and valid for as long as you offer spare parts or customer
|
||||
support for that product model, to give anyone who possesses the object code
|
||||
either (1) a copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical medium customarily
|
||||
used for software interchange, for a price no more than your reasonable cost
|
||||
of physically performing this conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the written
|
||||
offer to provide the Corresponding Source. This alternative is allowed only
|
||||
occasionally and noncommercially, and only if you received the object code
|
||||
with such an offer, in accord with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated place (gratis
|
||||
or for a charge), and offer equivalent access to the Corresponding Source
|
||||
in the same way through the same place at no further charge. You need not
|
||||
require recipients to copy the Corresponding Source along with the object
|
||||
code. If the place to copy the object code is a network server, the Corresponding
|
||||
Source may be on a different server (operated by you or a third party) that
|
||||
supports equivalent copying facilities, provided you maintain clear directions
|
||||
next to the object code saying where to find the Corresponding Source. Regardless
|
||||
of what server hosts the Corresponding Source, you remain obligated to ensure
|
||||
that it is available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided you inform
|
||||
other peers where the object code and Corresponding Source of the work are
|
||||
being offered to the general public at no charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded from
|
||||
the Corresponding Source as a System Library, need not be included in conveying
|
||||
the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any tangible
|
||||
personal property which is normally used for personal, family, or household
|
||||
purposes, or (2) anything designed or sold for incorporation into a dwelling.
|
||||
In determining whether a product is a consumer product, doubtful cases shall
|
||||
be resolved in favor of coverage. For a particular product received by a
|
||||
particular user, "normally used" refers to a typical or common use of that
|
||||
class of product, regardless of the status of the particular user or of the
|
||||
way in which the particular user actually uses, or expects or is expected
|
||||
to use, the product. A product is a consumer product regardless of whether
|
||||
the product has substantial commercial, industrial or non-consumer uses, unless
|
||||
such uses represent the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods, procedures,
|
||||
authorization keys, or other information required to install and execute modified
|
||||
versions of a covered work in that User Product from a modified version of
|
||||
its Corresponding Source. The information must suffice to ensure that the
|
||||
continued functioning of the modified object code is in no case prevented
|
||||
or interfered with solely because modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or specifically
|
||||
for use in, a User Product, and the conveying occurs as part of a transaction
|
||||
in which the right of possession and use of the User Product is transferred
|
||||
to the recipient in perpetuity or for a fixed term (regardless of how the
|
||||
transaction is characterized), the Corresponding Source conveyed under this
|
||||
section must be accompanied by the Installation Information. But this requirement
|
||||
does not apply if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has been installed
|
||||
in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a requirement
|
||||
to continue to provide support service, warranty, or updates for a work that
|
||||
has been modified or installed by the recipient, or for the User Product in
|
||||
which it has been modified or installed. Access to a network may be denied
|
||||
when the modification itself materially and adversely affects the operation
|
||||
of the network or violates the rules and protocols for communication across
|
||||
the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, in accord
|
||||
with this section must be in a format that is publicly documented (and with
|
||||
an implementation available to the public in source code form), and must require
|
||||
no special password or key for unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
"Additional permissions" are terms that supplement the terms of this License
|
||||
by making exceptions from one or more of its conditions. Additional permissions
|
||||
that are applicable to the entire Program shall be treated as though they
|
||||
were included in this License, to the extent that they are valid under applicable
|
||||
law. If additional permissions apply only to part of the Program, that part
|
||||
may be used separately under those permissions, but the entire Program remains
|
||||
governed by this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option remove any
|
||||
additional permissions from that copy, or from any part of it. (Additional
|
||||
permissions may be written to require their own removal in certain cases when
|
||||
you modify the work.) You may place additional permissions on material, added
|
||||
by you to a covered work, for which you have or can give appropriate copyright
|
||||
permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you add
|
||||
to a covered work, you may (if authorized by the copyright holders of that
|
||||
material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the terms of
|
||||
sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or author
|
||||
attributions in that material or in the Appropriate Legal Notices displayed
|
||||
by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or requiring
|
||||
that modified versions of such material be marked in reasonable ways as different
|
||||
from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or authors
|
||||
of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some trade names,
|
||||
trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that material by
|
||||
anyone who conveys the material (or modified versions of it) with contractual
|
||||
assumptions of liability to the recipient, for any liability that these contractual
|
||||
assumptions directly impose on those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further restrictions"
|
||||
within the meaning of section 10. If the Program as you received it, or any
|
||||
part of it, contains a notice stating that it is governed by this License
|
||||
along with a term that is a further restriction, you may remove that term.
|
||||
If a license document contains a further restriction but permits relicensing
|
||||
or conveying under this License, you may add to a covered work material governed
|
||||
by the terms of that license document, provided that the further restriction
|
||||
does not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you must place,
|
||||
in the relevant source files, a statement of the additional terms that apply
|
||||
to those files, or a notice indicating where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the form
|
||||
of a separately written license, or stated as exceptions; the above requirements
|
||||
apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly provided
|
||||
under this License. Any attempt otherwise to propagate or modify it is void,
|
||||
and will automatically terminate your rights under this License (including
|
||||
any patent licenses granted under the third paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your license from
|
||||
a particular copyright holder is reinstated (a) provisionally, unless and
|
||||
until the copyright holder explicitly and finally terminates your license,
|
||||
and (b) permanently, if the copyright holder fails to notify you of the violation
|
||||
by some reasonable means prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is reinstated permanently
|
||||
if the copyright holder notifies you of the violation by some reasonable means,
|
||||
this is the first time you have received notice of violation of this License
|
||||
(for any work) from that copyright holder, and you cure the violation prior
|
||||
to 30 days after your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the licenses
|
||||
of parties who have received copies or rights from you under this License.
|
||||
If your rights have been terminated and not permanently reinstated, you do
|
||||
not qualify to receive new licenses for the same material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or run a copy
|
||||
of the Program. Ancillary propagation of a covered work occurring solely
|
||||
as a consequence of using peer-to-peer transmission to receive a copy likewise
|
||||
does not require acceptance. However, nothing other than this License grants
|
||||
you permission to propagate or modify any covered work. These actions infringe
|
||||
copyright if you do not accept this License. Therefore, by modifying or propagating
|
||||
a covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically receives
|
||||
a license from the original licensors, to run, modify and propagate that work,
|
||||
subject to this License. You are not responsible for enforcing compliance
|
||||
by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an organization,
|
||||
or substantially all assets of one, or subdividing an organization, or merging
|
||||
organizations. If propagation of a covered work results from an entity transaction,
|
||||
each party to that transaction who receives a copy of the work also receives
|
||||
whatever licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the Corresponding
|
||||
Source of the work from the predecessor in interest, if the predecessor has
|
||||
it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the rights
|
||||
granted or affirmed under this License. For example, you may not impose a
|
||||
license fee, royalty, or other charge for exercise of rights granted under
|
||||
this License, and you may not initiate litigation (including a cross-claim
|
||||
or counterclaim in a lawsuit) alleging that any patent claim is infringed
|
||||
by making, using, selling, offering for sale, or importing the Program or
|
||||
any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this License
|
||||
of the Program or a work on which the Program is based. The work thus licensed
|
||||
is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims owned or controlled
|
||||
by the contributor, whether already acquired or hereafter acquired, that would
|
||||
be infringed by some manner, permitted by this License, of making, using,
|
||||
or selling its contributor version, but do not include claims that would be
|
||||
infringed only as a consequence of further modification of the contributor
|
||||
version. For purposes of this definition, "control" includes the right to
|
||||
grant patent sublicenses in a manner consistent with the requirements of this
|
||||
License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free patent
|
||||
license under the contributor's essential patent claims, to make, use, sell,
|
||||
offer for sale, import and otherwise run, modify and propagate the contents
|
||||
of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express agreement
|
||||
or commitment, however denominated, not to enforce a patent (such as an express
|
||||
permission to practice a patent or covenant not to sue for patent infringement).
|
||||
To "grant" such a patent license to a party means to make such an agreement
|
||||
or commitment not to enforce a patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, and the
|
||||
Corresponding Source of the work is not available for anyone to copy, free
|
||||
of charge and under the terms of this License, through a publicly available
|
||||
network server or other readily accessible means, then you must either (1)
|
||||
cause the Corresponding Source to be so available, or (2) arrange to deprive
|
||||
yourself of the benefit of the patent license for this particular work, or
|
||||
(3) arrange, in a manner consistent with the requirements of this License,
|
||||
to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have actual
|
||||
knowledge that, but for the patent license, your conveying the covered work
|
||||
in a country, or your recipient's use of the covered work in a country, would
|
||||
infringe one or more identifiable patents in that country that you have reason
|
||||
to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or arrangement,
|
||||
you convey, or propagate by procuring conveyance of, a covered work, and grant
|
||||
a patent license to some of the parties receiving the covered work authorizing
|
||||
them to use, propagate, modify or convey a specific copy of the covered work,
|
||||
then the patent license you grant is automatically extended to all recipients
|
||||
of the covered work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within the scope
|
||||
of its coverage, prohibits the exercise of, or is conditioned on the non-exercise
|
||||
of one or more of the rights that are specifically granted under this License.
|
||||
You may not convey a covered work if you are a party to an arrangement with
|
||||
a third party that is in the business of distributing software, under which
|
||||
you make payment to the third party based on the extent of your activity of
|
||||
conveying the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory patent
|
||||
license (a) in connection with copies of the covered work conveyed by you
|
||||
(or copies made from those copies), or (b) primarily for and in connection
|
||||
with specific products or compilations that contain the covered work, unless
|
||||
you entered into that arrangement, or that patent license was granted, prior
|
||||
to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting any implied
|
||||
license or other defenses to infringement that may otherwise be available
|
||||
to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or otherwise)
|
||||
that contradict the conditions of this License, they do not excuse you from
|
||||
the conditions of this License. If you cannot convey a covered work so as
|
||||
to satisfy simultaneously your obligations under this License and any other
|
||||
pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey the
|
||||
Program, the only way you could satisfy both those terms and this License
|
||||
would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the Program,
|
||||
your modified version must prominently offer all users interacting with it
|
||||
remotely through a computer network (if your version supports such interaction)
|
||||
an opportunity to receive the Corresponding Source of your version by providing
|
||||
access to the Corresponding Source from a network server at no charge, through
|
||||
some standard or customary means of facilitating copying of software. This
|
||||
Corresponding Source shall include the Corresponding Source for any work covered
|
||||
by version 3 of the GNU General Public License that is incorporated pursuant
|
||||
to the following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have permission to
|
||||
link or combine any covered work with a work licensed under version 3 of the
|
||||
GNU General Public License into a single combined work, and to convey the
|
||||
resulting work. The terms of this License will continue to apply to the part
|
||||
which is the covered work, but the work with which it is combined will remain
|
||||
governed by version 3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of the
|
||||
GNU Affero General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to address
|
||||
new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies
|
||||
that a certain numbered version of the GNU Affero General Public License "or
|
||||
any later version" applies to it, you have the option of following the terms
|
||||
and conditions either of that numbered version or of any later version published
|
||||
by the Free Software Foundation. If the Program does not specify a version
|
||||
number of the GNU Affero General Public License, you may choose any version
|
||||
ever published by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions of
|
||||
the GNU Affero General Public License can be used, that proxy's public statement
|
||||
of acceptance of a version permanently authorizes you to choose that version
|
||||
for the Program.
|
||||
|
||||
Later license versions may give you additional or different permissions.
|
||||
However, no additional obligations are imposed on any author or copyright
|
||||
holder as a result of your choosing to follow a later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
|
||||
LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
|
||||
AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR
|
||||
OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
|
||||
ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM
|
||||
AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO
|
||||
USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
|
||||
INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
|
||||
PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
|
||||
PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided above cannot
|
||||
be given local legal effect according to their terms, reviewing courts shall
|
||||
apply local law that most closely approximates an absolute waiver of all civil
|
||||
liability in connection with the Program, unless a warranty or assumption
|
||||
of liability accompanies a copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest possible
|
||||
use to the public, the best way to achieve this is to make it free software
|
||||
which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to attach
|
||||
them to the start of each source file to most effectively state the exclusion
|
||||
of warranty; and each file should have at least the "copyright" line and a
|
||||
pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License along
|
||||
with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer network,
|
||||
you should also make sure that it provides a way for users to get its source.
|
||||
For example, if your program is a web application, its interface could display
|
||||
a "Source" link that leads users to an archive of the code. There are many
|
||||
ways you could offer source, and different solutions will be better for different
|
||||
programs; see section 13 for the specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary. For
|
||||
more information on this, and how to apply and follow the GNU AGPL, see <http://www.gnu.org/licenses/>.
|
121
LICENSES/CC0-1.0.txt
Normal file
121
LICENSES/CC0-1.0.txt
Normal file
|
@ -0,0 +1,121 @@
|
|||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
3
LICENSES/GPL-3.0-linking-exception.txt
Normal file
3
LICENSES/GPL-3.0-linking-exception.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
Additional permission under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or combining it with [name of library] (or a modified version of that library), containing parts covered by the terms of [name of library's license], the licensors of this Program grant you additional permission to convey the resulting work.
|
20
LICENSES/MIT.txt
Normal file
20
LICENSES/MIT.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
41
Makefile
Normal file
41
Makefile
Normal file
|
@ -0,0 +1,41 @@
|
|||
# SPDX-License-Identifier: CC0-1.0
|
||||
# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
CARGO = cargo
|
||||
INSTALL = install
|
||||
INSTALL_PROGRAM = $(INSTALL)
|
||||
MKDIR_P = mkdir -p
|
||||
PROFILE = release
|
||||
|
||||
prefix = /usr/local
|
||||
exec_prefix = $(prefix)
|
||||
bindir = $(exec_prefix)/bin
|
||||
|
||||
all: release
|
||||
.PHONY: all
|
||||
|
||||
cargo-deps: vendor/github_schema.graphql src/merge_commit.graphql
|
||||
.PHONY: cargo-deps
|
||||
|
||||
target/release/pr-tracker: cargo-deps
|
||||
$(CARGO) build --release
|
||||
|
||||
target/debug/pr-tracker: cargo-deps
|
||||
$(CARGO) build
|
||||
|
||||
check: cargo-deps
|
||||
$(CARGO) test
|
||||
.PHONY: check
|
||||
|
||||
install-dirs:
|
||||
$(MKDIR_P) $(DESTDIR)$(bindir)
|
||||
.PHONY: install-dirs
|
||||
|
||||
install: install-dirs target/$(PROFILE)/pr-tracker
|
||||
$(INSTALL_PROGRAM) target/$(PROFILE)/pr-tracker \
|
||||
$(DESTDIR)$(bindir)/pr-tracker
|
||||
.PHONY: install
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(bindir)/pr-tracker
|
||||
.PHONY: uninstall
|
112
README
Normal file
112
README
Normal file
|
@ -0,0 +1,112 @@
|
|||
pr-tracker
|
||||
==========
|
||||
|
||||
Run a web server that displays the path a Nixpkgs pull request will
|
||||
take through the various release channels.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Build and runtime dependencies:
|
||||
- libsystemd
|
||||
- OpenSSL
|
||||
|
||||
Other build dependencies:
|
||||
- Cargo
|
||||
- rustc
|
||||
- pkg-config
|
||||
|
||||
Other runtime dependencies:
|
||||
- Git
|
||||
|
||||
In most cases, installation should be as simple as
|
||||
|
||||
make install
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The program must be supplied with a local checkout of the monitored
|
||||
git repository, the remote name in the repository corresponding to
|
||||
upstream Nixpkgs, a User-Agent string to use when contacting the
|
||||
GitHub API, and a URL where users can download the program's source
|
||||
code. Optionally, a "mount" path can be specified, which will be
|
||||
prefixed to all of the server's routes, so that it can be served at a
|
||||
non-root HTTP path.
|
||||
|
||||
For example:
|
||||
|
||||
pr-tracker \
|
||||
--path /var/lib/nixpkgs.git \
|
||||
--remote nixpkgs \
|
||||
--user-agent 'pr-tracker (alyssais)' \
|
||||
--source-url https://example.com/pr-tracker.tar.gz \
|
||||
--mount pr-tracker
|
||||
|
||||
Additionally, a GitHub API token should be supplied on pr-tracker's
|
||||
standard input.
|
||||
|
||||
pr-tracker expects the socket(s) for it to listen on to be set up for
|
||||
it by a service supervisor, using the systemd socket activation
|
||||
protocol. It does not support binding its own sockets, but it can
|
||||
still be run outside of systemd using by implementing the same
|
||||
interface using utility programs, such as in this example that makes
|
||||
use of the s6-networking[1] and execline[2] packages (example is
|
||||
written in POSIX shell, not execline):
|
||||
|
||||
s6-tcpserver 0.0.0.0 8000 \
|
||||
fdmove 3 0 \
|
||||
env LISTEN_FDS=1 \
|
||||
getpid LISTEN_PID
|
||||
redirfd -r 0 /var/lib/pr-tracker/token \
|
||||
pr-tracker [...]
|
||||
|
||||
Further information on available command line arguments can be
|
||||
obtained with
|
||||
|
||||
pr-tracker --help
|
||||
|
||||
[1]: https://skarnet.org/software/s6-networking/
|
||||
[2]: https://skarnet.org/software/execline/
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
The upstream git repository for pr-tracker is available at
|
||||
<https://git.qyliss.net/pr-tracker/>.
|
||||
|
||||
Bugs and patches can be sent to the author,
|
||||
Alyssa Ross <hi@alyssa.is>.
|
||||
|
||||
For information about how to use git to send a patch email, see
|
||||
<https://git-send-email.io/>.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation; either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public
|
||||
License along with this program; if not, see
|
||||
<https://www.gnu.org/licenses>.
|
||||
|
||||
Additional permission under GNU AGPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with OpenSSL (or a modified version of that library),
|
||||
containing parts covered by the terms of the OpenSSL License, or the
|
||||
Original SSLeay License, the licensors of this Program grant you
|
||||
additional permission to convey the resulting work.
|
2
README.license
Normal file
2
README.license
Normal file
|
@ -0,0 +1,2 @@
|
|||
SPDX-License-Identifier: CC0-1.0
|
||||
SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
6
build.rs
Normal file
6
build.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception
|
||||
// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-lib=systemd")
|
||||
}
|
62
src/branches.rs
Normal file
62
src/branches.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception
|
||||
// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::{Regex, RegexSet};
|
||||
|
||||
const NEXT_BRANCH_TABLE: [(&str, &str); 8] = [
|
||||
(r"\Astaging(-[\d.]+)?\z", "staging-next$1"),
|
||||
(r"\Astaging-next\z", "master"),
|
||||
(r"\Amaster\z", "nixpkgs-unstable"),
|
||||
(r"\Amaster\z", "nixos-unstable-small"),
|
||||
(r"\Anixos-(.*)-small\z", "nixos-$1"),
|
||||
(r"\Arelease-([\d.]+)\z", "nixpkgs-$1-darwin"),
|
||||
(r"\Arelease-([\d.]+)\z", "nixos-$1-small"),
|
||||
(r"\Astaging-next-([\d.]*)\z", "release-$1"),
|
||||
];
|
||||
|
||||
static BRANCH_NEXTS: Lazy<BTreeMap<&str, Vec<&str>>> = Lazy::new(|| {
|
||||
NEXT_BRANCH_TABLE
|
||||
.iter()
|
||||
.fold(BTreeMap::new(), |mut map, (pattern, next)| {
|
||||
map.entry(pattern).or_insert_with(Vec::new).push(next);
|
||||
map
|
||||
})
|
||||
});
|
||||
|
||||
static BRANCH_NEXTS_BY_INDEX: Lazy<Vec<&Vec<&str>>> = Lazy::new(|| BRANCH_NEXTS.values().collect());
|
||||
|
||||
static BRANCH_PATTERNS: Lazy<Vec<Regex>> = Lazy::new(|| {
|
||||
BRANCH_NEXTS
|
||||
.keys()
|
||||
.copied()
|
||||
.map(Regex::new)
|
||||
.map(Result::unwrap)
|
||||
.collect()
|
||||
});
|
||||
|
||||
static BRANCH_REGEXES: Lazy<RegexSet> = Lazy::new(|| RegexSet::new(BRANCH_NEXTS.keys()).unwrap());
|
||||
|
||||
pub fn next_branches(branch: &str) -> Vec<Cow<str>> {
|
||||
BRANCH_REGEXES
|
||||
.matches(branch)
|
||||
.iter()
|
||||
.flat_map(|index| {
|
||||
let regex = BRANCH_PATTERNS.get(index).unwrap();
|
||||
BRANCH_NEXTS_BY_INDEX
|
||||
.get(index)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(move |next| regex.replace(branch, *next))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_branches() {
|
||||
let res = next_branches("release-20.09");
|
||||
assert_eq!(res, vec!["nixpkgs-20.09-darwin", "nixos-20.09-small"])
|
||||
}
|
139
src/github.rs
Normal file
139
src/github.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception
|
||||
// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
use graphql_client::GraphQLQuery;
|
||||
use serde::Deserialize;
|
||||
use surf::http::headers::HeaderValue;
|
||||
use surf::StatusCode;
|
||||
|
||||
type GitObjectID = String;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
NotFound,
|
||||
Serialization(serde_json::Error),
|
||||
Request(surf::Error),
|
||||
Response(StatusCode),
|
||||
Deserialization(http_types::Error),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
use Error::*;
|
||||
match self {
|
||||
NotFound => write!(f, "Not found"),
|
||||
Serialization(e) => write!(f, "Serialization error: {}", e),
|
||||
Request(e) => write!(f, "Request error: {}", e),
|
||||
Response(s) => write!(f, "Unexpected response status: {}", s),
|
||||
Deserialization(e) => write!(f, "Deserialization error: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
#[derive(GraphQLQuery)]
|
||||
#[graphql(
|
||||
schema_path = "vendor/github_schema.graphql",
|
||||
query_path = "src/merge_commit.graphql",
|
||||
response_derives = "Debug"
|
||||
)]
|
||||
struct MergeCommitQuery;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GitHubGraphQLResponse<D> {
|
||||
data: D,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PullRequestStatus {
|
||||
Open,
|
||||
Closed,
|
||||
Merged {
|
||||
/// This field is optional because GitHub doesn't provide us with this information
|
||||
/// for PRs merged before around March 2016.
|
||||
merge_commit_oid: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MergeInfo {
|
||||
pub branch: String,
|
||||
pub status: PullRequestStatus,
|
||||
}
|
||||
|
||||
pub struct GitHub<'a> {
|
||||
token: &'a OsStr,
|
||||
user_agent: &'a OsStr,
|
||||
}
|
||||
|
||||
impl<'a> GitHub<'a> {
|
||||
pub fn new(token: &'a OsStr, user_agent: &'a OsStr) -> Self {
|
||||
Self { token, user_agent }
|
||||
}
|
||||
|
||||
fn authorization_header(&self) -> Result<HeaderValue, surf::Error> {
|
||||
let mut value = b"bearer ".to_vec();
|
||||
value.extend_from_slice(self.token.as_bytes());
|
||||
Ok(HeaderValue::from_bytes(value)?)
|
||||
}
|
||||
|
||||
pub async fn merge_info_for_nixpkgs_pr(&self, pr: i64) -> Result<MergeInfo, Error> {
|
||||
let query = MergeCommitQuery::build_query(merge_commit_query::Variables {
|
||||
owner: "NixOS".to_string(),
|
||||
repo: "nixpkgs".to_string(),
|
||||
number: pr,
|
||||
});
|
||||
|
||||
let response = surf::post("https://api.github.com/graphql")
|
||||
.header("Accept", "application/vnd.github.merge-info-preview+json")
|
||||
.header(
|
||||
"User-Agent",
|
||||
HeaderValue::from_bytes(self.user_agent.as_bytes().to_vec())
|
||||
.map_err(Error::Request)?,
|
||||
)
|
||||
.header(
|
||||
"Authorization",
|
||||
self.authorization_header().map_err(Error::Request)?,
|
||||
)
|
||||
.body(serde_json::to_vec(&query).map_err(Error::Serialization)?)
|
||||
.send()
|
||||
.await
|
||||
.map_err(Error::Request)?;
|
||||
|
||||
let status = response.status();
|
||||
if status == StatusCode::NotFound || status == StatusCode::Gone {
|
||||
return Err(Error::NotFound);
|
||||
} else if !status.is_success() {
|
||||
return Err(Error::Response(status));
|
||||
}
|
||||
|
||||
let data: GitHubGraphQLResponse<merge_commit_query::ResponseData> = dbg!(response)
|
||||
.body_json()
|
||||
.await
|
||||
.map_err(Error::Deserialization)?;
|
||||
|
||||
let pr = data
|
||||
.data
|
||||
.repository
|
||||
.and_then(|repo| repo.pull_request)
|
||||
.ok_or(Error::NotFound)?;
|
||||
|
||||
Ok(MergeInfo {
|
||||
branch: pr.base_ref_name,
|
||||
status: if pr.merged {
|
||||
PullRequestStatus::Merged {
|
||||
merge_commit_oid: pr.merge_commit.map(|commit| commit.oid),
|
||||
}
|
||||
} else if pr.closed {
|
||||
PullRequestStatus::Closed
|
||||
} else {
|
||||
PullRequestStatus::Open
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
211
src/main.rs
Normal file
211
src/main.rs
Normal file
|
@ -0,0 +1,211 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception
|
||||
// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
mod branches;
|
||||
mod github;
|
||||
mod nixpkgs;
|
||||
mod systemd;
|
||||
mod tree;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use askama::Template;
|
||||
use async_std::io;
|
||||
use async_std::net::TcpListener;
|
||||
use async_std::os::unix::io::FromRawFd;
|
||||
use async_std::os::unix::net::UnixListener;
|
||||
use async_std::pin::Pin;
|
||||
use async_std::prelude::*;
|
||||
use async_std::process::exit;
|
||||
use futures_util::future::join_all;
|
||||
use http_types::mime;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::Deserialize;
|
||||
use structopt::StructOpt;
|
||||
use tide::{Request, Response};
|
||||
|
||||
use github::{GitHub, PullRequestStatus};
|
||||
use nixpkgs::Nixpkgs;
|
||||
use systemd::{is_socket_inet, is_socket_unix, listen_fds};
|
||||
use tree::Tree;
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
struct Config {
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
path: PathBuf,
|
||||
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
remote: PathBuf,
|
||||
|
||||
#[structopt(long, parse(from_os_str))]
|
||||
user_agent: OsString,
|
||||
|
||||
#[structopt(long)]
|
||||
source_url: String,
|
||||
|
||||
#[structopt(long, default_value = "/")]
|
||||
mount: String,
|
||||
}
|
||||
|
||||
static CONFIG: Lazy<Config> = Lazy::new(Config::from_args);
|
||||
|
||||
static GITHUB_TOKEN: Lazy<OsString> = Lazy::new(|| {
|
||||
use std::io::{stdin, BufRead, BufReader};
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
let mut bytes = Vec::with_capacity(41);
|
||||
if let Err(e) = BufReader::new(stdin()).read_until(b'\n', &mut bytes) {
|
||||
eprintln!("pr-tracker: read: {}", e);
|
||||
exit(74)
|
||||
}
|
||||
if bytes.last() == Some(&b'\n') {
|
||||
bytes.pop();
|
||||
}
|
||||
OsString::from_vec(bytes)
|
||||
});
|
||||
|
||||
#[derive(Debug, Default, Template)]
|
||||
#[template(path = "page.html")]
|
||||
struct PageTemplate {
|
||||
error: Option<String>,
|
||||
pr_number: Option<String>,
|
||||
closed: bool,
|
||||
tree: Option<Tree>,
|
||||
source_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Query {
|
||||
pr: Option<String>,
|
||||
}
|
||||
|
||||
async fn track_pr(pr_number: Option<String>, status: &mut u16, page: &mut PageTemplate) {
|
||||
let pr_number = match pr_number {
|
||||
Some(pr_number) => pr_number,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let pr_number_i64 = match pr_number.parse() {
|
||||
Ok(n) => n,
|
||||
Err(_) => {
|
||||
*status = 400;
|
||||
page.error = Some(format!("Invalid PR number: {}", pr_number));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let github = GitHub::new(&GITHUB_TOKEN, &CONFIG.user_agent);
|
||||
|
||||
let merge_info = match github.merge_info_for_nixpkgs_pr(pr_number_i64).await {
|
||||
Err(github::Error::NotFound) => {
|
||||
*status = 404;
|
||||
page.error = Some(format!("No such nixpkgs PR #{}.", pr_number_i64));
|
||||
return;
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
*status = 500;
|
||||
page.error = Some(e.to_string());
|
||||
return;
|
||||
}
|
||||
|
||||
Ok(info) => info,
|
||||
};
|
||||
|
||||
page.pr_number = Some(pr_number);
|
||||
|
||||
if matches!(merge_info.status, PullRequestStatus::Closed) {
|
||||
page.closed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
let nixpkgs = Nixpkgs::new(&CONFIG.path, &CONFIG.remote);
|
||||
let tree = Tree::make(merge_info.branch.to_string(), &merge_info.status, &nixpkgs).await;
|
||||
|
||||
if let github::PullRequestStatus::Merged {
|
||||
merge_commit_oid, ..
|
||||
} = merge_info.status
|
||||
{
|
||||
if merge_commit_oid.is_none() {
|
||||
page.error = Some("For older PRs, GitHub doesn't tell us the merge commit, so we're unable to track this PR past being merged.".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
page.tree = Some(tree);
|
||||
}
|
||||
|
||||
async fn handle_request<S>(request: Request<S>) -> http_types::Result<Response> {
|
||||
let mut status = 200;
|
||||
let mut page = PageTemplate {
|
||||
source_url: CONFIG.source_url.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let pr_number = request.query::<Query>()?.pr;
|
||||
|
||||
track_pr(pr_number, &mut status, &mut page).await;
|
||||
|
||||
Ok(Response::builder(status)
|
||||
.content_type(mime::HTML)
|
||||
.body(page.render()?)
|
||||
.build())
|
||||
}
|
||||
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
fn handle_error<T, E>(result: Result<T, E>, code: i32, message: impl AsRef<str>) -> T
|
||||
where
|
||||
E: std::error::Error,
|
||||
{
|
||||
match result {
|
||||
Ok(v) => return v,
|
||||
Err(e) => {
|
||||
eprintln!("pr-tracker: {}: {}", message.as_ref(), e);
|
||||
exit(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure arguments are parsed before starting server.
|
||||
let _ = *CONFIG;
|
||||
let _ = *GITHUB_TOKEN;
|
||||
|
||||
let mut server = tide::new();
|
||||
let mut root = server.at(&CONFIG.mount);
|
||||
|
||||
root.at("/").get(handle_request);
|
||||
|
||||
let fd_count = handle_error(listen_fds(true), 71, "sd_listen_fds");
|
||||
|
||||
if fd_count == 0 {
|
||||
eprintln!("pr-tracker: No listen file descriptors given");
|
||||
exit(64);
|
||||
}
|
||||
|
||||
let mut listeners: Vec<Pin<Box<dyn Future<Output = _>>>> = Vec::new();
|
||||
|
||||
for fd in (3..).into_iter().take(fd_count as usize) {
|
||||
let s = server.clone();
|
||||
if handle_error(is_socket_inet(fd), 74, "sd_is_socket_inet") {
|
||||
listeners.push(Box::pin(s.listen(unsafe { TcpListener::from_raw_fd(fd) })));
|
||||
} else if handle_error(is_socket_unix(fd), 74, "sd_is_socket_unix") {
|
||||
listeners.push(Box::pin(s.listen(unsafe { UnixListener::from_raw_fd(fd) })));
|
||||
} else {
|
||||
eprintln!("pr-tracker: file descriptor {} is not a socket", fd);
|
||||
exit(64);
|
||||
}
|
||||
}
|
||||
|
||||
let errors: Vec<_> = join_all(listeners)
|
||||
.await
|
||||
.into_iter()
|
||||
.filter_map(io::Result::err)
|
||||
.collect();
|
||||
for error in errors.iter() {
|
||||
eprintln!("pr-tracker: listen: {}", error);
|
||||
}
|
||||
if !errors.is_empty() {
|
||||
exit(74);
|
||||
}
|
||||
}
|
15
src/merge_commit.graphql
Normal file
15
src/merge_commit.graphql
Normal file
|
@ -0,0 +1,15 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception
|
||||
# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
query MergeCommitQuery($owner: String!, $repo: String!, $number: Int!) {
|
||||
repository(owner: $owner, name: $repo) {
|
||||
pullRequest(number: $number) {
|
||||
baseRefName
|
||||
mergeCommit {
|
||||
oid
|
||||
}
|
||||
merged
|
||||
closed
|
||||
}
|
||||
}
|
||||
}
|
127
src/nixpkgs.rs
Normal file
127
src/nixpkgs.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception
|
||||
// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::ffi::OsStr;
|
||||
use std::ffi::OsString;
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::os::unix::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitStatus;
|
||||
|
||||
use async_std::io;
|
||||
use async_std::process::{Command, Stdio};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Io(io::Error),
|
||||
ExitFailure(ExitStatus),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
use Error::*;
|
||||
match self {
|
||||
Io(e) => write!(f, "git: {}", e),
|
||||
ExitFailure(e) => match e.code() {
|
||||
Some(code) => write!(f, "git exited {}", code),
|
||||
None => write!(f, "git killed by signal {}", e.signal().unwrap()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
fn check_status(status: ExitStatus) -> Result<()> {
|
||||
if status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ExitFailure(status))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Nixpkgs<'a> {
|
||||
path: &'a Path,
|
||||
remote_name: &'a Path,
|
||||
}
|
||||
|
||||
impl<'a> Nixpkgs<'a> {
|
||||
pub fn new(path: &'a Path, remote_name: &'a Path) -> Self {
|
||||
Self { path, remote_name }
|
||||
}
|
||||
|
||||
fn git_command(&self, subcommand: impl AsRef<OsStr>) -> Command {
|
||||
let mut command = Command::new("git");
|
||||
command.arg("-C");
|
||||
command.arg(&self.path);
|
||||
command.arg(subcommand);
|
||||
command
|
||||
}
|
||||
|
||||
async fn git_branch_contains(&self, commit: &str) -> Result<Vec<u8>> {
|
||||
let output = self
|
||||
.git_command("branch")
|
||||
.args(&["-r", "--format=%(refname)", "--contains"])
|
||||
.arg(commit)
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.await
|
||||
.map_err(Error::Io)?;
|
||||
|
||||
check_status(output.status)?;
|
||||
|
||||
Ok(output.stdout)
|
||||
}
|
||||
|
||||
async fn git_fetch_nixpkgs(&self) -> Result<()> {
|
||||
// TODO: add refspecs
|
||||
self.git_command("fetch")
|
||||
.arg(&self.remote_name)
|
||||
.status()
|
||||
.await
|
||||
.map_err(Error::Io)
|
||||
.and_then(check_status)
|
||||
}
|
||||
|
||||
pub async fn branches_containing_commit(
|
||||
&self,
|
||||
commit: &str,
|
||||
out: &mut BTreeSet<OsString>,
|
||||
) -> Result<()> {
|
||||
let output = match self.git_branch_contains(commit).await {
|
||||
Err(Error::ExitFailure(status)) if status.code().is_some() => {
|
||||
eprintln!("pr-tracker: git branch --contains failed; updating branches");
|
||||
|
||||
if let Err(e) = self.git_fetch_nixpkgs().await {
|
||||
eprintln!("pr-tracker: fetching nixpkgs: {}", e);
|
||||
// Carry on, because it might have fetched what we
|
||||
// need before dying.
|
||||
}
|
||||
|
||||
self.git_branch_contains(commit).await?
|
||||
}
|
||||
|
||||
Ok(output) => output,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let mut prefix = PathBuf::from("refs/remotes/");
|
||||
prefix.push(&self.remote_name);
|
||||
|
||||
for branch_name in output
|
||||
.split(|byte| *byte == b'\n')
|
||||
.filter(|b| !b.is_empty())
|
||||
.map(OsStr::from_bytes)
|
||||
.map(Path::new)
|
||||
.filter_map(|r| r.strip_prefix(&prefix).ok())
|
||||
.map(Into::into)
|
||||
{
|
||||
out.insert(branch_name);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
49
src/systemd.rs
Normal file
49
src/systemd.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception
|
||||
// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
use std::io;
|
||||
use std::os::raw::{c_char, c_int, c_uint};
|
||||
use std::os::unix::prelude::*;
|
||||
use std::ptr::null;
|
||||
|
||||
extern "C" {
|
||||
fn sd_listen_fds(unset_environment: c_int) -> c_int;
|
||||
fn sd_is_socket_inet(
|
||||
fd: c_int,
|
||||
family: c_int,
|
||||
type_: c_int,
|
||||
listening: c_int,
|
||||
port: u16,
|
||||
) -> c_int;
|
||||
fn sd_is_socket_unix(
|
||||
fd: c_int,
|
||||
type_: c_int,
|
||||
listening: c_int,
|
||||
path: *const c_char,
|
||||
length: usize,
|
||||
) -> c_int;
|
||||
}
|
||||
|
||||
pub fn listen_fds(unset_environment: bool) -> io::Result<c_uint> {
|
||||
let r = unsafe { sd_listen_fds(if unset_environment { 1 } else { 0 }) };
|
||||
if r < 0 {
|
||||
return Err(io::Error::from_raw_os_error(-r));
|
||||
}
|
||||
Ok(r as c_uint)
|
||||
}
|
||||
|
||||
pub fn is_socket_inet(fd: RawFd) -> io::Result<bool> {
|
||||
let r = unsafe { sd_is_socket_inet(fd, 0, 0, -1, 0) };
|
||||
if r < 0 {
|
||||
return Err(io::Error::from_raw_os_error(-r));
|
||||
}
|
||||
Ok(r != 0)
|
||||
}
|
||||
|
||||
pub fn is_socket_unix(fd: RawFd) -> io::Result<bool> {
|
||||
let r = unsafe { sd_is_socket_unix(fd, 0, -1, null(), 0) };
|
||||
if r < 0 {
|
||||
return Err(io::Error::from_raw_os_error(-r));
|
||||
}
|
||||
Ok(r != 0)
|
||||
}
|
91
src/tree.rs
Normal file
91
src/tree.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception
|
||||
// SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is>
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
|
||||
use askama::Template;
|
||||
|
||||
use crate::branches::next_branches;
|
||||
use crate::github;
|
||||
use crate::nixpkgs::Nixpkgs;
|
||||
|
||||
#[derive(Debug, Template)]
|
||||
#[template(path = "tree.html")]
|
||||
pub struct Tree {
|
||||
branch_name: String,
|
||||
accepted: Option<bool>,
|
||||
children: Vec<Tree>,
|
||||
}
|
||||
|
||||
impl Tree {
|
||||
fn generate(branch: String, found_branches: &mut BTreeSet<OsString>) -> Tree {
|
||||
found_branches.insert((&branch).into());
|
||||
|
||||
let nexts = next_branches(&branch)
|
||||
.into_iter()
|
||||
.map(|b| Self::generate(b.to_string(), found_branches))
|
||||
.collect();
|
||||
|
||||
Tree {
|
||||
accepted: None,
|
||||
branch_name: branch,
|
||||
children: nexts,
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_accepted(&mut self, branches: &BTreeSet<OsString>, missing_means_absent: bool) {
|
||||
self.accepted = match branches.contains(OsStr::new(&self.branch_name)) {
|
||||
true => Some(true),
|
||||
false if missing_means_absent => Some(false),
|
||||
false => None,
|
||||
};
|
||||
|
||||
for child in self.children.iter_mut() {
|
||||
child.fill_accepted(branches, missing_means_absent);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn make(base_branch: String, merge_status: &github::PullRequestStatus, nixpkgs: &Nixpkgs<'_>) -> Tree {
|
||||
let mut missing_means_absent = true;
|
||||
let mut branches = BTreeSet::new();
|
||||
|
||||
let mut tree = Self::generate(base_branch.clone(), &mut branches);
|
||||
|
||||
if let github::PullRequestStatus::Merged {
|
||||
merge_commit_oid, ..
|
||||
} = merge_status
|
||||
{
|
||||
if let Some(merge_commit) = merge_commit_oid {
|
||||
let mut containing_commits = BTreeSet::new();
|
||||
|
||||
if let Err(e) =
|
||||
nixpkgs.branches_containing_commit(&merge_commit, &mut containing_commits)
|
||||
.await
|
||||
{
|
||||
eprintln!("pr-tracker: branches_containing_commit: {}", e);
|
||||
missing_means_absent = false;
|
||||
}
|
||||
|
||||
branches = branches
|
||||
.intersection(&containing_commits)
|
||||
.cloned()
|
||||
.collect();
|
||||
} else {
|
||||
branches.clear();
|
||||
missing_means_absent = false;
|
||||
}
|
||||
|
||||
// Even if something goes wrong with our local Git repo,
|
||||
// or GitHub didn't tell us the merge commit, we know that
|
||||
// the base branch of the PR must contain the commit,
|
||||
// because GitHub told us it was merged into it.
|
||||
branches.insert(base_branch.into());
|
||||
} else {
|
||||
branches.clear();
|
||||
}
|
||||
|
||||
tree.fill_accepted(&branches, missing_means_absent);
|
||||
tree
|
||||
}
|
||||
}
|
203
templates/page.html
Normal file
203
templates/page.html
Normal file
|
@ -0,0 +1,203 @@
|
|||
<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception -->
|
||||
<!-- SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is> -->
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{% match pr_number %}
|
||||
{% when Some with (pr_number) %}
|
||||
<title>Nixpkgs PR #{{ pr_number }} progress</title>
|
||||
{% else %}
|
||||
<title>Nixpkgs PR progress tracker</title>
|
||||
{% endmatch %}
|
||||
|
||||
<meta charset="utf-8">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
line-height: 1;
|
||||
font-family: sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
body > header {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
#pr {
|
||||
width: 6ch;
|
||||
box-sizing: content-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
body > section {
|
||||
background: #c4b0b0;
|
||||
padding: 0 1em;
|
||||
margin: 1em auto;
|
||||
display: flex;
|
||||
max-width: 50ch;
|
||||
}
|
||||
|
||||
body > main {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
body > main > ol {
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul > li {
|
||||
margin-left: 2em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ul > li:last-child {
|
||||
margin-left: 0;
|
||||
position: static;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 1em 0;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
span {
|
||||
color: transparent;
|
||||
position: relative;
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
display: inline-block;
|
||||
margin-right: 0.5em;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
span::after {
|
||||
content: "";
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
border: .3em solid #7A877D;
|
||||
color: white;
|
||||
text-align: center;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
span.state-pending::after {
|
||||
background: #C2C9C2;
|
||||
}
|
||||
|
||||
span.state-unknown::after {
|
||||
background: #C4A500;
|
||||
content: "?";
|
||||
}
|
||||
|
||||
span.state-accepted::after {
|
||||
background: #00C42D;
|
||||
content: "✔";
|
||||
}
|
||||
|
||||
span.state-rejected::after {
|
||||
background: #c40000;
|
||||
content: "❌︎";
|
||||
}
|
||||
|
||||
ul span::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 42.5%;
|
||||
bottom: 42.5%;
|
||||
right: .5em;
|
||||
left: -1em;
|
||||
display: block;
|
||||
background: #7A877D;
|
||||
}
|
||||
|
||||
ul > li:last-child > span::before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
ol {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ol::before, ul::before {
|
||||
background: #7A877D;
|
||||
content: "";
|
||||
display: block;
|
||||
left: .85em;
|
||||
top: 0.5em;
|
||||
bottom: 1em;
|
||||
width: .3em;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1>Nixpkgs Pull Request Tracker</h1>
|
||||
|
||||
<form>
|
||||
<label for="pr">PR number: </label>
|
||||
<input id="pr" name="pr" type="text" pattern="[1-9][0-9]*"
|
||||
value="{%- match pr_number -%}
|
||||
{%- when Some with (pr_number) -%}
|
||||
{{- pr_number -}}
|
||||
{%- else -%}
|
||||
{%- endmatch -%}">
|
||||
<button type="submit">Track</button>
|
||||
</form>
|
||||
</header>
|
||||
|
||||
{% match error %}
|
||||
{% when Some with (error) %}
|
||||
<section>
|
||||
<p>{{ error }}</p>
|
||||
</section>
|
||||
{% else %}
|
||||
{% endmatch %}
|
||||
|
||||
{% match pr_number %}
|
||||
{% when Some with (pr_number) %}
|
||||
<main>
|
||||
<ol>
|
||||
<li>
|
||||
{% if closed %}
|
||||
<span class="state-rejected">❌</span>
|
||||
{% else %}
|
||||
<span class="state-accepted">✅</span>
|
||||
{% endif %}
|
||||
PR <a href="https://github.com/NixOS/nixpkgs/pull/{{ pr_number }}">#{{ pr_number }}</a>
|
||||
{% if closed %}
|
||||
closed
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
{% match tree %}
|
||||
{% when Some with (tree) %}
|
||||
{{ tree|safe }}
|
||||
{% else %}
|
||||
{% endmatch %}
|
||||
</ol>
|
||||
</main>
|
||||
{% else %}
|
||||
{% endmatch %}
|
||||
|
||||
<footer>
|
||||
<p>By <a href="https://alyssa.is/">Alyssa Ross</a></p>
|
||||
|
||||
<p><a href="{{ source_url }}">Source code</a></p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
23
templates/tree.html
Normal file
23
templates/tree.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!-- SPDX-License-Identifier: AGPL-3.0-or-later WITH GPL-3.0-linking-exception -->
|
||||
<!-- SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is> -->
|
||||
|
||||
<li>
|
||||
{% match accepted %}
|
||||
{% when Some with (true) %}
|
||||
<span class="state-accepted">✅</span>
|
||||
{% when Some with (false) %}
|
||||
<span class="state-pending">⚪</span>
|
||||
{% when None %}
|
||||
<span class="state-unknown">❓</span>
|
||||
{% endmatch %}
|
||||
|
||||
{{ branch_name }}
|
||||
|
||||
{% if !children.is_empty() %}
|
||||
<ul>
|
||||
{% for child in children %}
|
||||
{{ child|safe }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
39245
vendor/github_schema.graphql
vendored
Normal file
39245
vendor/github_schema.graphql
vendored
Normal file
File diff suppressed because it is too large
Load diff
2
vendor/github_schema.graphql.license
vendored
Normal file
2
vendor/github_schema.graphql.license
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
SPDX-License-Identifier: MIT
|
||||
SPDX-FileCopyrightText: 2017 Gregor Martynus
|
Loading…
Reference in a new issue