Signed by hannaSSH key fingerprint: SHA256:4g9cWhkAAw8gwqhJUcVbSnGEvdGwklW+1Aa/fMUu59k
.gitignore +8 −0
| @@ -0,0 +1,8 @@ | |||
| 1 | /target | ||
| 2 | result | ||
| 3 | result-* | ||
| 4 | .direnv/ | ||
| 5 | *.db | ||
| 6 | *.db-wal | ||
| 7 | *.db-shm | ||
| 8 | .env | ||
Cargo.lock +54 −0
| @@ -0,0 +1,54 @@ | |||
| 1 | # This file is automatically @generated by Cargo. | ||
| 2 | # It is not intended for manual editing. | ||
| 3 | version = 4 | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "api" | ||
| 7 | version = "0.1.0" | ||
| 8 | |||
| 9 | [[package]] | ||
| 10 | name = "auth" | ||
| 11 | version = "0.1.0" | ||
| 12 | |||
| 13 | [[package]] | ||
| 14 | name = "cli" | ||
| 15 | version = "0.1.0" | ||
| 16 | |||
| 17 | [[package]] | ||
| 18 | name = "config" | ||
| 19 | version = "0.1.0" | ||
| 20 | |||
| 21 | [[package]] | ||
| 22 | name = "fabrica" | ||
| 23 | version = "0.1.0" | ||
| 24 | dependencies = [ | ||
| 25 | "cli", | ||
| 26 | ] | ||
| 27 | |||
| 28 | [[package]] | ||
| 29 | name = "git" | ||
| 30 | version = "0.1.0" | ||
| 31 | |||
| 32 | [[package]] | ||
| 33 | name = "highlight" | ||
| 34 | version = "0.1.0" | ||
| 35 | |||
| 36 | [[package]] | ||
| 37 | name = "mail" | ||
| 38 | version = "0.1.0" | ||
| 39 | |||
| 40 | [[package]] | ||
| 41 | name = "model" | ||
| 42 | version = "0.1.0" | ||
| 43 | |||
| 44 | [[package]] | ||
| 45 | name = "ssh" | ||
| 46 | version = "0.1.0" | ||
| 47 | |||
| 48 | [[package]] | ||
| 49 | name = "store" | ||
| 50 | version = "0.1.0" | ||
| 51 | |||
| 52 | [[package]] | ||
| 53 | name = "web" | ||
| 54 | version = "0.1.0" | ||
Cargo.toml +65 −0
| @@ -0,0 +1,65 @@ | |||
| 1 | [package] | ||
| 2 | name = "fabrica" | ||
| 3 | description = "A self-hosted git server: a single binary serving repos over HTTPS and SSH with a themeable web UI." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [[bin]] | ||
| 13 | name = "fabrica" | ||
| 14 | path = "src/main.rs" | ||
| 15 | |||
| 16 | [dependencies] | ||
| 17 | cli = { path = "crates/cli" } | ||
| 18 | |||
| 19 | [lints] | ||
| 20 | workspace = true | ||
| 21 | |||
| 22 | [workspace] | ||
| 23 | members = ["crates/*"] | ||
| 24 | resolver = "3" | ||
| 25 | |||
| 26 | [workspace.package] | ||
| 27 | version = "0.1.0" | ||
| 28 | edition = "2024" | ||
| 29 | rust-version = "1.85" | ||
| 30 | license = "MPL-2.0" | ||
| 31 | authors = ["fabrica contributors"] | ||
| 32 | repository = "https://git.example.com/fabrica" | ||
| 33 | publish = false | ||
| 34 | |||
| 35 | [workspace.dependencies] | ||
| 36 | # In-tree crates. Dependency direction is strictly one-way and enforced: | ||
| 37 | # model <- store / git / auth <- web / api / ssh / cli | ||
| 38 | # No crate may depend on `web`; `model` depends on nothing in-tree. | ||
| 39 | config = { path = "crates/config" } | ||
| 40 | store = { path = "crates/store" } | ||
| 41 | model = { path = "crates/model" } | ||
| 42 | git = { path = "crates/git" } | ||
| 43 | highlight = { path = "crates/highlight" } | ||
| 44 | auth = { path = "crates/auth" } | ||
| 45 | mail = { path = "crates/mail" } | ||
| 46 | ssh = { path = "crates/ssh" } | ||
| 47 | web = { path = "crates/web" } | ||
| 48 | api = { path = "crates/api" } | ||
| 49 | cli = { path = "crates/cli" } | ||
| 50 | |||
| 51 | [workspace.lints.rust] | ||
| 52 | unsafe_code = "forbid" | ||
| 53 | missing_docs = "warn" | ||
| 54 | |||
| 55 | [workspace.lints.clippy] | ||
| 56 | all = { level = "deny", priority = -1 } | ||
| 57 | pedantic = { level = "warn", priority = -1 } | ||
| 58 | unwrap_used = "deny" | ||
| 59 | expect_used = "warn" | ||
| 60 | panic = "deny" | ||
| 61 | todo = "deny" | ||
| 62 | |||
| 63 | [profile.release] | ||
| 64 | strip = true | ||
| 65 | lto = "thin" | ||
LICENSE +373 −0
| @@ -0,0 +1,373 @@ | |||
| 1 | Mozilla Public License Version 2.0 | ||
| 2 | ================================== | ||
| 3 | |||
| 4 | 1. Definitions | ||
| 5 | -------------- | ||
| 6 | |||
| 7 | 1.1. "Contributor" | ||
| 8 | means each individual or legal entity that creates, contributes to | ||
| 9 | the creation of, or owns Covered Software. | ||
| 10 | |||
| 11 | 1.2. "Contributor Version" | ||
| 12 | means the combination of the Contributions of others (if any) used | ||
| 13 | by a Contributor and that particular Contributor's Contribution. | ||
| 14 | |||
| 15 | 1.3. "Contribution" | ||
| 16 | means Covered Software of a particular Contributor. | ||
| 17 | |||
| 18 | 1.4. "Covered Software" | ||
| 19 | means Source Code Form to which the initial Contributor has attached | ||
| 20 | the notice in Exhibit A, the Executable Form of such Source Code | ||
| 21 | Form, and Modifications of such Source Code Form, in each case | ||
| 22 | including portions thereof. | ||
| 23 | |||
| 24 | 1.5. "Incompatible With Secondary Licenses" | ||
| 25 | means | ||
| 26 | |||
| 27 | (a) that the initial Contributor has attached the notice described | ||
| 28 | in Exhibit B to the Covered Software; or | ||
| 29 | |||
| 30 | (b) that the Covered Software was made available under the terms of | ||
| 31 | version 1.1 or earlier of the License, but not also under the | ||
| 32 | terms of a Secondary License. | ||
| 33 | |||
| 34 | 1.6. "Executable Form" | ||
| 35 | means any form of the work other than Source Code Form. | ||
| 36 | |||
| 37 | 1.7. "Larger Work" | ||
| 38 | means a work that combines Covered Software with other material, in | ||
| 39 | a separate file or files, that is not Covered Software. | ||
| 40 | |||
| 41 | 1.8. "License" | ||
| 42 | means this document. | ||
| 43 | |||
| 44 | 1.9. "Licensable" | ||
| 45 | means having the right to grant, to the maximum extent possible, | ||
| 46 | whether at the time of the initial grant or subsequently, any and | ||
| 47 | all of the rights conveyed by this License. | ||
| 48 | |||
| 49 | 1.10. "Modifications" | ||
| 50 | means any of the following: | ||
| 51 | |||
| 52 | (a) any file in Source Code Form that results from an addition to, | ||
| 53 | deletion from, or modification of the contents of Covered | ||
| 54 | Software; or | ||
| 55 | |||
| 56 | (b) any new file in Source Code Form that contains any Covered | ||
| 57 | Software. | ||
| 58 | |||
| 59 | 1.11. "Patent Claims" of a Contributor | ||
| 60 | means any patent claim(s), including without limitation, method, | ||
| 61 | process, and apparatus claims, in any patent Licensable by such | ||
| 62 | Contributor that would be infringed, but for the grant of the | ||
| 63 | License, by the making, using, selling, offering for sale, having | ||
| 64 | made, import, or transfer of either its Contributions or its | ||
| 65 | Contributor Version. | ||
| 66 | |||
| 67 | 1.12. "Secondary License" | ||
| 68 | means either the GNU General Public License, Version 2.0, the GNU | ||
| 69 | Lesser General Public License, Version 2.1, the GNU Affero General | ||
| 70 | Public License, Version 3.0, or any later versions of those | ||
| 71 | licenses. | ||
| 72 | |||
| 73 | 1.13. "Source Code Form" | ||
| 74 | means the form of the work preferred for making modifications. | ||
| 75 | |||
| 76 | 1.14. "You" (or "Your") | ||
| 77 | means an individual or a legal entity exercising rights under this | ||
| 78 | License. For legal entities, "You" includes any entity that | ||
| 79 | controls, is controlled by, or is under common control with You. For | ||
| 80 | purposes of this definition, "control" means (a) the power, direct | ||
| 81 | or indirect, to cause the direction or management of such entity, | ||
| 82 | whether by contract or otherwise, or (b) ownership of more than | ||
| 83 | fifty percent (50%) of the outstanding shares or beneficial | ||
| 84 | ownership of such entity. | ||
| 85 | |||
| 86 | 2. License Grants and Conditions | ||
| 87 | -------------------------------- | ||
| 88 | |||
| 89 | 2.1. Grants | ||
| 90 | |||
| 91 | Each Contributor hereby grants You a world-wide, royalty-free, | ||
| 92 | non-exclusive license: | ||
| 93 | |||
| 94 | (a) under intellectual property rights (other than patent or trademark) | ||
| 95 | Licensable by such Contributor to use, reproduce, make available, | ||
| 96 | modify, display, perform, distribute, and otherwise exploit its | ||
| 97 | Contributions, either on an unmodified basis, with Modifications, or | ||
| 98 | as part of a Larger Work; and | ||
| 99 | |||
| 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer | ||
| 101 | for sale, have made, import, and otherwise transfer either its | ||
| 102 | Contributions or its Contributor Version. | ||
| 103 | |||
| 104 | 2.2. Effective Date | ||
| 105 | |||
| 106 | The licenses granted in Section 2.1 with respect to any Contribution | ||
| 107 | become effective for each Contribution on the date the Contributor first | ||
| 108 | distributes such Contribution. | ||
| 109 | |||
| 110 | 2.3. Limitations on Grant Scope | ||
| 111 | |||
| 112 | The licenses granted in this Section 2 are the only rights granted under | ||
| 113 | this License. No additional rights or licenses will be implied from the | ||
| 114 | distribution or licensing of Covered Software under this License. | ||
| 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a | ||
| 116 | Contributor: | ||
| 117 | |||
| 118 | (a) for any code that a Contributor has removed from Covered Software; | ||
| 119 | or | ||
| 120 | |||
| 121 | (b) for infringements caused by: (i) Your and any other third party's | ||
| 122 | modifications of Covered Software, or (ii) the combination of its | ||
| 123 | Contributions with other software (except as part of its Contributor | ||
| 124 | Version); or | ||
| 125 | |||
| 126 | (c) under Patent Claims infringed by Covered Software in the absence of | ||
| 127 | its Contributions. | ||
| 128 | |||
| 129 | This License does not grant any rights in the trademarks, service marks, | ||
| 130 | or logos of any Contributor (except as may be necessary to comply with | ||
| 131 | the notice requirements in Section 3.4). | ||
| 132 | |||
| 133 | 2.4. Subsequent Licenses | ||
| 134 | |||
| 135 | No Contributor makes additional grants as a result of Your choice to | ||
| 136 | distribute the Covered Software under a subsequent version of this | ||
| 137 | License (see Section 10.2) or under the terms of a Secondary License (if | ||
| 138 | permitted under the terms of Section 3.3). | ||
| 139 | |||
| 140 | 2.5. Representation | ||
| 141 | |||
| 142 | Each Contributor represents that the Contributor believes its | ||
| 143 | Contributions are its original creation(s) or it has sufficient rights | ||
| 144 | to grant the rights to its Contributions conveyed by this License. | ||
| 145 | |||
| 146 | 2.6. Fair Use | ||
| 147 | |||
| 148 | This License is not intended to limit any rights You have under | ||
| 149 | applicable copyright doctrines of fair use, fair dealing, or other | ||
| 150 | equivalents. | ||
| 151 | |||
| 152 | 2.7. Conditions | ||
| 153 | |||
| 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted | ||
| 155 | in Section 2.1. | ||
| 156 | |||
| 157 | 3. Responsibilities | ||
| 158 | ------------------- | ||
| 159 | |||
| 160 | 3.1. Distribution of Source Form | ||
| 161 | |||
| 162 | All distribution of Covered Software in Source Code Form, including any | ||
| 163 | Modifications that You create or to which You contribute, must be under | ||
| 164 | the terms of this License. You must inform recipients that the Source | ||
| 165 | Code Form of the Covered Software is governed by the terms of this | ||
| 166 | License, and how they can obtain a copy of this License. You may not | ||
| 167 | attempt to alter or restrict the recipients' rights in the Source Code | ||
| 168 | Form. | ||
| 169 | |||
| 170 | 3.2. Distribution of Executable Form | ||
| 171 | |||
| 172 | If You distribute Covered Software in Executable Form then: | ||
| 173 | |||
| 174 | (a) such Covered Software must also be made available in Source Code | ||
| 175 | Form, as described in Section 3.1, and You must inform recipients of | ||
| 176 | the Executable Form how they can obtain a copy of such Source Code | ||
| 177 | Form by reasonable means in a timely manner, at a charge no more | ||
| 178 | than the cost of distribution to the recipient; and | ||
| 179 | |||
| 180 | (b) You may distribute such Executable Form under the terms of this | ||
| 181 | License, or sublicense it under different terms, provided that the | ||
| 182 | license for the Executable Form does not attempt to limit or alter | ||
| 183 | the recipients' rights in the Source Code Form under this License. | ||
| 184 | |||
| 185 | 3.3. Distribution of a Larger Work | ||
| 186 | |||
| 187 | You may create and distribute a Larger Work under terms of Your choice, | ||
| 188 | provided that You also comply with the requirements of this License for | ||
| 189 | the Covered Software. If the Larger Work is a combination of Covered | ||
| 190 | Software with a work governed by one or more Secondary Licenses, and the | ||
| 191 | Covered Software is not Incompatible With Secondary Licenses, this | ||
| 192 | License permits You to additionally distribute such Covered Software | ||
| 193 | under the terms of such Secondary License(s), so that the recipient of | ||
| 194 | the Larger Work may, at their option, further distribute the Covered | ||
| 195 | Software under the terms of either this License or such Secondary | ||
| 196 | License(s). | ||
| 197 | |||
| 198 | 3.4. Notices | ||
| 199 | |||
| 200 | You may not remove or alter the substance of any license notices | ||
| 201 | (including copyright notices, patent notices, disclaimers of warranty, | ||
| 202 | or limitations of liability) contained within the Source Code Form of | ||
| 203 | the Covered Software, except that You may alter any license notices to | ||
| 204 | the extent required to remedy known factual inaccuracies. | ||
| 205 | |||
| 206 | 3.5. Application of Additional Terms | ||
| 207 | |||
| 208 | You may choose to offer, and to charge a fee for, warranty, support, | ||
| 209 | indemnity or liability obligations to one or more recipients of Covered | ||
| 210 | Software. However, You may do so only on Your own behalf, and not on | ||
| 211 | behalf of any Contributor. You must make it absolutely clear that any | ||
| 212 | such warranty, support, indemnity, or liability obligation is offered by | ||
| 213 | You alone, and You hereby agree to indemnify every Contributor for any | ||
| 214 | liability incurred by such Contributor as a result of warranty, support, | ||
| 215 | indemnity or liability terms You offer. You may include additional | ||
| 216 | disclaimers of warranty and limitations of liability specific to any | ||
| 217 | jurisdiction. | ||
| 218 | |||
| 219 | 4. Inability to Comply Due to Statute or Regulation | ||
| 220 | --------------------------------------------------- | ||
| 221 | |||
| 222 | If it is impossible for You to comply with any of the terms of this | ||
| 223 | License with respect to some or all of the Covered Software due to | ||
| 224 | statute, judicial order, or regulation then You must: (a) comply with | ||
| 225 | the terms of this License to the maximum extent possible; and (b) | ||
| 226 | describe the limitations and the code they affect. Such description must | ||
| 227 | be placed in a text file included with all distributions of the Covered | ||
| 228 | Software under this License. Except to the extent prohibited by statute | ||
| 229 | or regulation, such description must be sufficiently detailed for a | ||
| 230 | recipient of ordinary skill to be able to understand it. | ||
| 231 | |||
| 232 | 5. Termination | ||
| 233 | -------------- | ||
| 234 | |||
| 235 | 5.1. The rights granted under this License will terminate automatically | ||
| 236 | if You fail to comply with any of its terms. However, if You become | ||
| 237 | compliant, then the rights granted under this License from a particular | ||
| 238 | Contributor are reinstated (a) provisionally, unless and until such | ||
| 239 | Contributor explicitly and finally terminates Your grants, and (b) on an | ||
| 240 | ongoing basis, if such Contributor fails to notify You of the | ||
| 241 | non-compliance by some reasonable means prior to 60 days after You have | ||
| 242 | come back into compliance. Moreover, Your grants from a particular | ||
| 243 | Contributor are reinstated on an ongoing basis if such Contributor | ||
| 244 | notifies You of the non-compliance by some reasonable means, this is the | ||
| 245 | first time You have received notice of non-compliance with this License | ||
| 246 | from such Contributor, and You become compliant prior to 30 days after | ||
| 247 | Your receipt of the notice. | ||
| 248 | |||
| 249 | 5.2. If You initiate litigation against any entity by asserting a patent | ||
| 250 | infringement claim (excluding declaratory judgment actions, | ||
| 251 | counter-claims, and cross-claims) alleging that a Contributor Version | ||
| 252 | directly or indirectly infringes any patent, then the rights granted to | ||
| 253 | You by any and all Contributors for the Covered Software under Section | ||
| 254 | 2.1 of this License shall terminate. | ||
| 255 | |||
| 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all | ||
| 257 | end user license agreements (excluding distributors and resellers) which | ||
| 258 | have been validly granted by You or Your distributors under this License | ||
| 259 | prior to termination shall survive termination. | ||
| 260 | |||
| 261 | ************************************************************************ | ||
| 262 | * * | ||
| 263 | * 6. Disclaimer of Warranty * | ||
| 264 | * ------------------------- * | ||
| 265 | * * | ||
| 266 | * Covered Software is provided under this License on an "as is" * | ||
| 267 | * basis, without warranty of any kind, either expressed, implied, or * | ||
| 268 | * statutory, including, without limitation, warranties that the * | ||
| 269 | * Covered Software is free of defects, merchantable, fit for a * | ||
| 270 | * particular purpose or non-infringing. The entire risk as to the * | ||
| 271 | * quality and performance of the Covered Software is with You. * | ||
| 272 | * Should any Covered Software prove defective in any respect, You * | ||
| 273 | * (not any Contributor) assume the cost of any necessary servicing, * | ||
| 274 | * repair, or correction. This disclaimer of warranty constitutes an * | ||
| 275 | * essential part of this License. No use of any Covered Software is * | ||
| 276 | * authorized under this License except under this disclaimer. * | ||
| 277 | * * | ||
| 278 | ************************************************************************ | ||
| 279 | |||
| 280 | ************************************************************************ | ||
| 281 | * * | ||
| 282 | * 7. Limitation of Liability * | ||
| 283 | * -------------------------- * | ||
| 284 | * * | ||
| 285 | * Under no circumstances and under no legal theory, whether tort * | ||
| 286 | * (including negligence), contract, or otherwise, shall any * | ||
| 287 | * Contributor, or anyone who distributes Covered Software as * | ||
| 288 | * permitted above, be liable to You for any direct, indirect, * | ||
| 289 | * special, incidental, or consequential damages of any character * | ||
| 290 | * including, without limitation, damages for lost profits, loss of * | ||
| 291 | * goodwill, work stoppage, computer failure or malfunction, or any * | ||
| 292 | * and all other commercial damages or losses, even if such party * | ||
| 293 | * shall have been informed of the possibility of such damages. This * | ||
| 294 | * limitation of liability shall not apply to liability for death or * | ||
| 295 | * personal injury resulting from such party's negligence to the * | ||
| 296 | * extent applicable law prohibits such limitation. Some * | ||
| 297 | * jurisdictions do not allow the exclusion or limitation of * | ||
| 298 | * incidental or consequential damages, so this exclusion and * | ||
| 299 | * limitation may not apply to You. * | ||
| 300 | * * | ||
| 301 | ************************************************************************ | ||
| 302 | |||
| 303 | 8. Litigation | ||
| 304 | ------------- | ||
| 305 | |||
| 306 | Any litigation relating to this License may be brought only in the | ||
| 307 | courts of a jurisdiction where the defendant maintains its principal | ||
| 308 | place of business and such litigation shall be governed by laws of that | ||
| 309 | jurisdiction, without reference to its conflict-of-law provisions. | ||
| 310 | Nothing in this Section shall prevent a party's ability to bring | ||
| 311 | cross-claims or counter-claims. | ||
| 312 | |||
| 313 | 9. Miscellaneous | ||
| 314 | ---------------- | ||
| 315 | |||
| 316 | This License represents the complete agreement concerning the subject | ||
| 317 | matter hereof. If any provision of this License is held to be | ||
| 318 | unenforceable, such provision shall be reformed only to the extent | ||
| 319 | necessary to make it enforceable. Any law or regulation which provides | ||
| 320 | that the language of a contract shall be construed against the drafter | ||
| 321 | shall not be used to construe this License against a Contributor. | ||
| 322 | |||
| 323 | 10. Versions of the License | ||
| 324 | --------------------------- | ||
| 325 | |||
| 326 | 10.1. New Versions | ||
| 327 | |||
| 328 | Mozilla Foundation is the license steward. Except as provided in Section | ||
| 329 | 10.3, no one other than the license steward has the right to modify or | ||
| 330 | publish new versions of this License. Each version will be given a | ||
| 331 | distinguishing version number. | ||
| 332 | |||
| 333 | 10.2. Effect of New Versions | ||
| 334 | |||
| 335 | You may distribute the Covered Software under the terms of the version | ||
| 336 | of the License under which You originally received the Covered Software, | ||
| 337 | or under the terms of any subsequent version published by the license | ||
| 338 | steward. | ||
| 339 | |||
| 340 | 10.3. Modified Versions | ||
| 341 | |||
| 342 | If you create software not governed by this License, and you want to | ||
| 343 | create a new license for such software, you may create and use a | ||
| 344 | modified version of this License if you rename the license and remove | ||
| 345 | any references to the name of the license steward (except to note that | ||
| 346 | such modified license differs from this License). | ||
| 347 | |||
| 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary | ||
| 349 | Licenses | ||
| 350 | |||
| 351 | If You choose to distribute Source Code Form that is Incompatible With | ||
| 352 | Secondary Licenses under the terms of this version of the License, the | ||
| 353 | notice described in Exhibit B of this License must be attached. | ||
| 354 | |||
| 355 | Exhibit A - Source Code Form License Notice | ||
| 356 | ------------------------------------------- | ||
| 357 | |||
| 358 | This Source Code Form is subject to the terms of the Mozilla Public | ||
| 359 | License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 360 | file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 361 | |||
| 362 | If it is not possible or desirable to put the notice in a particular | ||
| 363 | file, then You may include the notice in a location (such as a LICENSE | ||
| 364 | file in a relevant directory) where a recipient would be likely to look | ||
| 365 | for such a notice. | ||
| 366 | |||
| 367 | You may add additional accurate notices of copyright ownership. | ||
| 368 | |||
| 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice | ||
| 370 | --------------------------------------------------------- | ||
| 371 | |||
| 372 | This Source Code Form is "Incompatible With Secondary Licenses", as | ||
| 373 | defined by the Mozilla Public License, v. 2.0. | ||
crates/api/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "api" | ||
| 3 | description = "JSON API served under /api/v1." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/api/src/lib.rs +5 −0
| @@ -0,0 +1,5 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! The JSON API surface. | ||
crates/auth/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "auth" | ||
| 3 | description = "Password hashing, sessions, JWTs, and the permission model." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/auth/src/lib.rs +5 −0
| @@ -0,0 +1,5 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! Authentication and authorization: argon2id, sessions, JWTs, and `access()`. | ||
crates/cli/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "cli" | ||
| 3 | description = "clap command tree and subcommand implementations." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/cli/src/lib.rs +20 −0
| @@ -0,0 +1,20 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! The command-line interface for fabrica. | ||
| 6 | //! | ||
| 7 | //! Every subcommand operates directly on the store and filesystem; there is no | ||
| 8 | //! IPC with a running `serve`. [`run`] is the single entry point invoked by the | ||
| 9 | //! `fabrica` binary. | ||
| 10 | |||
| 11 | use std::process::ExitCode; | ||
| 12 | |||
| 13 | /// Parse arguments and dispatch to the requested subcommand. | ||
| 14 | /// | ||
| 15 | /// Returns the process exit code. The command tree is built out over the | ||
| 16 | /// implementation phases; today this is a placeholder that exits successfully. | ||
| 17 | #[must_use] | ||
| 18 | pub fn run() -> ExitCode { | ||
| 19 | ExitCode::SUCCESS | ||
| 20 | } | ||
crates/config/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "config" | ||
| 3 | description = "Configuration loading and validation (TOML + environment overrides)." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/config/src/lib.rs +5 −0
| @@ -0,0 +1,5 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! Configuration loading, validation, and path resolution. | ||
crates/git/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "git" | ||
| 3 | description = "Git object reads, attribute resolution, signatures, and pack-transport spawning." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/git/src/lib.rs +5 −0
| @@ -0,0 +1,5 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! The git layer: in-process object reads via libgit2 and spawned pack transport. | ||
crates/highlight/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "highlight" | ||
| 3 | description = "Syntax highlighting with line-addressable, diff-aware output." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/highlight/src/lib.rs +5 −0
| @@ -0,0 +1,5 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! Syntax highlighting producing independently-valid HTML per line. | ||
crates/mail/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "mail" | ||
| 3 | description = "SMTP delivery and message templates." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/mail/src/lib.rs +5 −0
| @@ -0,0 +1,5 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! Outbound mail: templates rendered to text and HTML, sent best-effort. | ||
crates/model/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "model" | ||
| 3 | description = "Shared domain types with no I/O." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/model/src/lib.rs +5 −0
| @@ -0,0 +1,5 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! Shared domain types. This crate performs no I/O and depends on nothing else in-tree. | ||
crates/ssh/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "ssh" | ||
| 3 | description = "SSH server exposing git pack transport over the exec channel." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/ssh/src/lib.rs +5 −0
| @@ -0,0 +1,5 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! The SSH front-end: publickey auth and `exec` dispatch into pack processes. | ||
crates/store/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "store" | ||
| 3 | description = "Database schema, migrations, and queries for SQLite and Postgres." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/store/src/lib.rs +5 −0
| @@ -0,0 +1,5 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! Persistence layer: schema, migrations, and portable queries over SQLite and Postgres. | ||
crates/web/Cargo.toml +15 −0
| @@ -0,0 +1,15 @@ | |||
| 1 | [package] | ||
| 2 | name = "web" | ||
| 3 | description = "Axum router, maud templates, htmx partials, assets, and theming." | ||
| 4 | version.workspace = true | ||
| 5 | edition.workspace = true | ||
| 6 | rust-version.workspace = true | ||
| 7 | license.workspace = true | ||
| 8 | authors.workspace = true | ||
| 9 | repository.workspace = true | ||
| 10 | publish.workspace = true | ||
| 11 | |||
| 12 | [dependencies] | ||
| 13 | |||
| 14 | [lints] | ||
| 15 | workspace = true | ||
crates/web/src/lib.rs +5 −0
| @@ -0,0 +1,5 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! The web UI: server-rendered maud templates enhanced with htmx. | ||
deny.toml +37 −0
| @@ -0,0 +1,37 @@ | |||
| 1 | # cargo-deny configuration. Run via `nix flake check` (checks.deny) or | ||
| 2 | # `cargo deny check` in the dev shell. | ||
| 3 | |||
| 4 | [graph] | ||
| 5 | all-features = true | ||
| 6 | |||
| 7 | [advisories] | ||
| 8 | version = 2 | ||
| 9 | yanked = "deny" | ||
| 10 | |||
| 11 | [licenses] | ||
| 12 | version = 2 | ||
| 13 | # Permit the common permissive licenses plus MPL-2.0 (our own). | ||
| 14 | allow = [ | ||
| 15 | "MPL-2.0", | ||
| 16 | "MIT", | ||
| 17 | "Apache-2.0", | ||
| 18 | "Apache-2.0 WITH LLVM-exception", | ||
| 19 | "BSD-2-Clause", | ||
| 20 | "BSD-3-Clause", | ||
| 21 | "ISC", | ||
| 22 | "Unicode-3.0", | ||
| 23 | "Zlib", | ||
| 24 | "CC0-1.0", | ||
| 25 | ] | ||
| 26 | confidence-threshold = 0.9 | ||
| 27 | |||
| 28 | [bans] | ||
| 29 | multiple-versions = "warn" | ||
| 30 | wildcards = "deny" | ||
| 31 | # In-tree crates are referenced by path without a version; that is not a real | ||
| 32 | # wildcard dependency. | ||
| 33 | allow-wildcard-paths = true | ||
| 34 | |||
| 35 | [sources] | ||
| 36 | unknown-registry = "deny" | ||
| 37 | unknown-git = "deny" | ||
docs/decisions.md +87 −0
| @@ -0,0 +1,87 @@ | |||
| 1 | # Decisions log | ||
| 2 | |||
| 3 | A running log of design decisions: date, decision, alternatives considered, and | ||
| 4 | rationale. Seeded with the decisions the spec (`spec.md`) already makes. Update | ||
| 5 | this file on any deviation from the spec (a **SHOULD** deviation **MUST** be | ||
| 6 | logged here). | ||
| 7 | |||
| 8 | --- | ||
| 9 | |||
| 10 | ## 2026-07-24 — Pack transport via `git` subprocess | ||
| 11 | |||
| 12 | **Decision:** Server-side clone/fetch/push is handled by spawning the `git` | ||
| 13 | binary (`git upload-pack` / `git receive-pack`), not by libgit2. | ||
| 14 | |||
| 15 | **Alternatives:** Implement the smart pack protocol on top of libgit2's object | ||
| 16 | layer; use a pure-Rust reimplementation (`gitoxide`). | ||
| 17 | |||
| 18 | **Rationale:** libgit2 is client-only for transport — it has no server-side | ||
| 19 | `upload-pack`/`receive-pack`. Spawning `git` is the supported, correct path. | ||
| 20 | libgit2 (via `git2`) is retained for in-process reads (refs, trees, blobs, log, | ||
| 21 | diff, signatures, `init_bare`). Consequence: `git` (≥ 2.41) MUST be on `PATH` at | ||
| 22 | runtime; the Nix derivation wraps the binary with git on PATH. | ||
| 23 | |||
| 24 | ## 2026-07-24 — Repositories stored by id, not name | ||
| 25 | |||
| 26 | **Decision:** On disk, repos live at `{repo_dir}/{id[0..2]}/{id}.git` where `id` | ||
| 27 | is a lowercase ULID. The database is the only name→path authority. | ||
| 28 | |||
| 29 | **Alternatives:** Store repos by `{owner}/{name}.git` mirroring the URL. | ||
| 30 | |||
| 31 | **Rationale:** Renames and group moves become metadata-only (a DB update), never | ||
| 32 | touching the filesystem. Tradeoff: the tree is not human-browsable; | ||
| 33 | `fabrica repo path <user> <name>` resolves it for operators. | ||
| 34 | |||
| 35 | ## 2026-07-24 — `/-/` route separator | ||
| 36 | |||
| 37 | **Decision:** Web and API routes disambiguate variable-length repo paths from | ||
| 38 | sub-resources with the GitLab `/-/` convention: everything before `/-/` is the | ||
| 39 | repo path, everything after is the view. | ||
| 40 | |||
| 41 | **Alternatives:** Fixed-depth paths; a query parameter for the view. | ||
| 42 | |||
| 43 | **Rationale:** Group nesting makes repo paths of unknown depth. `/-/` is an | ||
| 44 | unambiguous, well-understood separator. | ||
| 45 | |||
| 46 | ## 2026-07-24 — Timestamps as epoch milliseconds | ||
| 47 | |||
| 48 | **Decision:** All timestamps are stored as `BIGINT` unix milliseconds (UTC). No | ||
| 49 | `DATETIME` / `TIMESTAMPTZ`. | ||
| 50 | |||
| 51 | **Alternatives:** Native date/time column types per dialect. | ||
| 52 | |||
| 53 | **Rationale:** One portable representation across SQLite and Postgres, ordered by | ||
| 54 | plain integer comparison. Ids are `TEXT` ULIDs (lexicographically sortable) used | ||
| 55 | for tie-breaking and ordering. | ||
| 56 | |||
| 57 | ## 2026-07-24 — `serve` runs in the foreground; no `stop` subcommand | ||
| 58 | |||
| 59 | **Decision:** `fabrica serve` runs in the foreground by default and is stopped by | ||
| 60 | signalling its pid (`SIGTERM`/`SIGINT`). `--detach` double-forks and writes a pid | ||
| 61 | file. There is no `server start` / `server stop` pair. | ||
| 62 | |||
| 63 | **Alternatives:** A daemonizing `start`/`stop`/`restart` command set. | ||
| 64 | |||
| 65 | **Rationale:** Foreground is correct for systemd and Docker, which own process | ||
| 66 | lifecycle. This deviates from an earlier `server start`/`server stop` sketch. | ||
| 67 | |||
| 68 | ## 2026-07-24 — Linear search, no index | ||
| 69 | |||
| 70 | **Decision:** Search (in-repo and global) is a linear scan over repo contents at | ||
| 71 | request time, with worker-pool, time-budget, and result-count bounds. | ||
| 72 | |||
| 73 | **Alternatives:** Maintain a `tantivy` full-text index. | ||
| 74 | |||
| 75 | **Rationale:** Appropriate for a small, private instance. A `tantivy` index is | ||
| 76 | the intended path if linear scan stops being fast enough. | ||
| 77 | |||
| 78 | ## 2026-07-24 — Nix flake uses flake-parts | ||
| 79 | |||
| 80 | **Decision:** The flake is structured with `flake-parts` (module system, | ||
| 81 | `perSystem`) rather than `flake-utils`. | ||
| 82 | |||
| 83 | **Alternatives:** `flake-utils.lib.eachDefaultSystem`. | ||
| 84 | |||
| 85 | **Rationale:** flake-parts scales better as the flake grows (NixOS module, | ||
| 86 | container image, multiple checks) and gives a cleaner multi-output structure. The | ||
| 87 | spec permits either; picked one and stay consistent. | ||
flake.lock +116 −0
| @@ -0,0 +1,116 @@ | |||
| 1 | { | ||
| 2 | "nodes": { | ||
| 3 | "crane": { | ||
| 4 | "locked": { | ||
| 5 | "lastModified": 1784564969, | ||
| 6 | "narHash": "sha256-TkTWNhJV/8Wk3czlbz4bwHZkFCaCHPsOy+oJe4PUiXg=", | ||
| 7 | "owner": "ipetkov", | ||
| 8 | "repo": "crane", | ||
| 9 | "rev": "7930f6c291de6f83c257839d434592aa085f290a", | ||
| 10 | "type": "github" | ||
| 11 | }, | ||
| 12 | "original": { | ||
| 13 | "owner": "ipetkov", | ||
| 14 | "repo": "crane", | ||
| 15 | "type": "github" | ||
| 16 | } | ||
| 17 | }, | ||
| 18 | "fenix": { | ||
| 19 | "inputs": { | ||
| 20 | "nixpkgs": [ | ||
| 21 | "nixpkgs" | ||
| 22 | ], | ||
| 23 | "rust-analyzer-src": "rust-analyzer-src" | ||
| 24 | }, | ||
| 25 | "locked": { | ||
| 26 | "lastModified": 1784882000, | ||
| 27 | "narHash": "sha256-uexExdN1itvHYHFek4bpvRqURvxYLKWUetpekP+udbk=", | ||
| 28 | "owner": "nix-community", | ||
| 29 | "repo": "fenix", | ||
| 30 | "rev": "42d2c5f54f90dd11ebd5dc7732f004aa900cfecd", | ||
| 31 | "type": "github" | ||
| 32 | }, | ||
| 33 | "original": { | ||
| 34 | "owner": "nix-community", | ||
| 35 | "repo": "fenix", | ||
| 36 | "type": "github" | ||
| 37 | } | ||
| 38 | }, | ||
| 39 | "flake-parts": { | ||
| 40 | "inputs": { | ||
| 41 | "nixpkgs-lib": "nixpkgs-lib" | ||
| 42 | }, | ||
| 43 | "locked": { | ||
| 44 | "lastModified": 1782949081, | ||
| 45 | "narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=", | ||
| 46 | "owner": "hercules-ci", | ||
| 47 | "repo": "flake-parts", | ||
| 48 | "rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e", | ||
| 49 | "type": "github" | ||
| 50 | }, | ||
| 51 | "original": { | ||
| 52 | "owner": "hercules-ci", | ||
| 53 | "repo": "flake-parts", | ||
| 54 | "type": "github" | ||
| 55 | } | ||
| 56 | }, | ||
| 57 | "nixpkgs": { | ||
| 58 | "locked": { | ||
| 59 | "lastModified": 1784796856, | ||
| 60 | "narHash": "sha256-wWFrV5/Qbm+lyt5x20E/bSbfJiGKMo4RCxZV8cl/WZI=", | ||
| 61 | "owner": "NixOS", | ||
| 62 | "repo": "nixpkgs", | ||
| 63 | "rev": "e2587caef70cea85dd97d7daab492899902dbf5d", | ||
| 64 | "type": "github" | ||
| 65 | }, | ||
| 66 | "original": { | ||
| 67 | "owner": "NixOS", | ||
| 68 | "ref": "nixos-unstable", | ||
| 69 | "repo": "nixpkgs", | ||
| 70 | "type": "github" | ||
| 71 | } | ||
| 72 | }, | ||
| 73 | "nixpkgs-lib": { | ||
| 74 | "locked": { | ||
| 75 | "lastModified": 1782614948, | ||
| 76 | "narHash": "sha256-ePjCwr1sNm9NYUqywL7QfK3JnlS015msC+eBu2zKlp8=", | ||
| 77 | "owner": "nix-community", | ||
| 78 | "repo": "nixpkgs.lib", | ||
| 79 | "rev": "db3f255737b94216eb71cce308e2912cf6bc2d7c", | ||
| 80 | "type": "github" | ||
| 81 | }, | ||
| 82 | "original": { | ||
| 83 | "owner": "nix-community", | ||
| 84 | "repo": "nixpkgs.lib", | ||
| 85 | "type": "github" | ||
| 86 | } | ||
| 87 | }, | ||
| 88 | "root": { | ||
| 89 | "inputs": { | ||
| 90 | "crane": "crane", | ||
| 91 | "fenix": "fenix", | ||
| 92 | "flake-parts": "flake-parts", | ||
| 93 | "nixpkgs": "nixpkgs" | ||
| 94 | } | ||
| 95 | }, | ||
| 96 | "rust-analyzer-src": { | ||
| 97 | "flake": false, | ||
| 98 | "locked": { | ||
| 99 | "lastModified": 1784667699, | ||
| 100 | "narHash": "sha256-853teJQOgTTKRfNxcIJ53ziJOGNeg3c74PvQl5l61Jc=", | ||
| 101 | "owner": "rust-lang", | ||
| 102 | "repo": "rust-analyzer", | ||
| 103 | "rev": "1174734d9c6453d13d2b5e3d578512c579ca37f1", | ||
| 104 | "type": "github" | ||
| 105 | }, | ||
| 106 | "original": { | ||
| 107 | "owner": "rust-lang", | ||
| 108 | "ref": "nightly", | ||
| 109 | "repo": "rust-analyzer", | ||
| 110 | "type": "github" | ||
| 111 | } | ||
| 112 | } | ||
| 113 | }, | ||
| 114 | "root": "root", | ||
| 115 | "version": 7 | ||
| 116 | } | ||
flake.nix +155 −0
| @@ -0,0 +1,155 @@ | |||
| 1 | { | ||
| 2 | description = "fabrica — a self-hosted git server (single binary, HTTPS + SSH, themeable web UI)"; | ||
| 3 | |||
| 4 | inputs = { | ||
| 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; | ||
| 6 | flake-parts.url = "github:hercules-ci/flake-parts"; | ||
| 7 | fenix = { | ||
| 8 | url = "github:nix-community/fenix"; | ||
| 9 | inputs.nixpkgs.follows = "nixpkgs"; | ||
| 10 | }; | ||
| 11 | crane.url = "github:ipetkov/crane"; | ||
| 12 | }; | ||
| 13 | |||
| 14 | outputs = | ||
| 15 | inputs@{ flake-parts, ... }: | ||
| 16 | flake-parts.lib.mkFlake { inherit inputs; } { | ||
| 17 | systems = [ | ||
| 18 | "x86_64-linux" | ||
| 19 | "aarch64-linux" | ||
| 20 | "x86_64-darwin" | ||
| 21 | "aarch64-darwin" | ||
| 22 | ]; | ||
| 23 | |||
| 24 | perSystem = | ||
| 25 | { self' | ||
| 26 | , pkgs | ||
| 27 | , system | ||
| 28 | , ... | ||
| 29 | }: | ||
| 30 | let | ||
| 31 | # Nightly toolchain via fenix, pinned through flake.lock. The `complete` | ||
| 32 | # profile carries clippy, rustfmt, rust-src, and rust-analyzer. | ||
| 33 | fenixToolchain = inputs.fenix.packages.${system}.complete.toolchain; | ||
| 34 | |||
| 35 | craneLib = (inputs.crane.mkLib pkgs).overrideToolchain fenixToolchain; | ||
| 36 | |||
| 37 | commonArgs = { | ||
| 38 | src = craneLib.cleanCargoSource ./.; | ||
| 39 | strictDeps = true; | ||
| 40 | nativeBuildInputs = [ | ||
| 41 | pkgs.pkg-config | ||
| 42 | pkgs.makeWrapper | ||
| 43 | ]; | ||
| 44 | buildInputs = [ | ||
| 45 | pkgs.libgit2 | ||
| 46 | pkgs.zlib | ||
| 47 | pkgs.openssl | ||
| 48 | ]; | ||
| 49 | # Use the system libgit2 rather than the vendored copy. | ||
| 50 | LIBGIT2_NO_VENDOR = "1"; | ||
| 51 | }; | ||
| 52 | |||
| 53 | cargoArtifacts = craneLib.buildDepsOnly commonArgs; | ||
| 54 | |||
| 55 | fabrica = craneLib.buildPackage ( | ||
| 56 | commonArgs | ||
| 57 | // { | ||
| 58 | inherit cargoArtifacts; | ||
| 59 | doCheck = false; # tests run as their own flake check | ||
| 60 | # The git plumbing must always be reachable at runtime. | ||
| 61 | postInstall = '' | ||
| 62 | wrapProgram $out/bin/fabrica \ | ||
| 63 | --prefix PATH : ${pkgs.lib.makeBinPath [ pkgs.git ]} | ||
| 64 | ''; | ||
| 65 | } | ||
| 66 | ); | ||
| 67 | in | ||
| 68 | { | ||
| 69 | packages = { | ||
| 70 | default = fabrica; | ||
| 71 | fabrica = fabrica; | ||
| 72 | |||
| 73 | container = pkgs.dockerTools.buildLayeredImage { | ||
| 74 | name = "fabrica"; | ||
| 75 | tag = "latest"; | ||
| 76 | contents = [ | ||
| 77 | fabrica | ||
| 78 | pkgs.git | ||
| 79 | pkgs.cacert | ||
| 80 | ]; | ||
| 81 | config = { | ||
| 82 | Entrypoint = [ "${fabrica}/bin/fabrica" ]; | ||
| 83 | Cmd = [ "serve" ]; | ||
| 84 | ExposedPorts = { | ||
| 85 | "8080/tcp" = { }; | ||
| 86 | "2222/tcp" = { }; | ||
| 87 | }; | ||
| 88 | Volumes = { | ||
| 89 | "/var/lib/fabrica" = { }; | ||
| 90 | }; | ||
| 91 | }; | ||
| 92 | }; | ||
| 93 | }; | ||
| 94 | |||
| 95 | checks = { | ||
| 96 | inherit fabrica; | ||
| 97 | |||
| 98 | clippy = craneLib.cargoClippy ( | ||
| 99 | commonArgs | ||
| 100 | // { | ||
| 101 | inherit cargoArtifacts; | ||
| 102 | cargoClippyExtraArgs = "--workspace --all-targets -- -D warnings"; | ||
| 103 | } | ||
| 104 | ); | ||
| 105 | |||
| 106 | fmt = craneLib.cargoFmt { | ||
| 107 | src = craneLib.cleanCargoSource ./.; | ||
| 108 | }; | ||
| 109 | |||
| 110 | test = craneLib.cargoNextest ( | ||
| 111 | commonArgs | ||
| 112 | // { | ||
| 113 | inherit cargoArtifacts; | ||
| 114 | partitions = 1; | ||
| 115 | partitionType = "count"; | ||
| 116 | # Crates have no tests during early scaffolding; do not treat an | ||
| 117 | # empty test set as a failure. | ||
| 118 | cargoNextestExtraArgs = "--no-tests=pass"; | ||
| 119 | } | ||
| 120 | ); | ||
| 121 | |||
| 122 | deny = craneLib.cargoDeny { | ||
| 123 | src = craneLib.cleanCargoSource ./.; | ||
| 124 | }; | ||
| 125 | |||
| 126 | doc = craneLib.cargoDoc ( | ||
| 127 | commonArgs | ||
| 128 | // { | ||
| 129 | inherit cargoArtifacts; | ||
| 130 | } | ||
| 131 | ); | ||
| 132 | }; | ||
| 133 | |||
| 134 | devShells.default = craneLib.devShell { | ||
| 135 | checks = self'.checks; | ||
| 136 | packages = [ | ||
| 137 | pkgs.sqlx-cli | ||
| 138 | pkgs.sqlite | ||
| 139 | pkgs.postgresql | ||
| 140 | pkgs.git | ||
| 141 | pkgs.openssh | ||
| 142 | pkgs.gnupg | ||
| 143 | pkgs.cargo-nextest | ||
| 144 | pkgs.cargo-deny | ||
| 145 | pkgs.just | ||
| 146 | pkgs.pkg-config | ||
| 147 | ]; | ||
| 148 | buildInputs = commonArgs.buildInputs; | ||
| 149 | LIBGIT2_NO_VENDOR = "1"; | ||
| 150 | }; | ||
| 151 | |||
| 152 | formatter = pkgs.nixpkgs-fmt; | ||
| 153 | }; | ||
| 154 | }; | ||
| 155 | } | ||
justfile +33 −0
| @@ -0,0 +1,33 @@ | |||
| 1 | # fabrica task runner. `just check` is the quality gate that MUST pass after | ||
| 2 | # every unit of work. `nix flake check` is the superset. | ||
| 3 | |||
| 4 | # List available recipes. | ||
| 5 | default: | ||
| 6 | @just --list | ||
| 7 | |||
| 8 | # The quality gate: format, check, lint, test. Run after every unit of work. | ||
| 9 | check: fmt-check build clippy test | ||
| 10 | |||
| 11 | # Verify formatting without modifying files. | ||
| 12 | fmt-check: | ||
| 13 | cargo fmt --all -- --check | ||
| 14 | |||
| 15 | # Format the workspace in place. | ||
| 16 | fmt: | ||
| 17 | cargo fmt --all | ||
| 18 | |||
| 19 | # Type-check the whole workspace, including tests and examples. | ||
| 20 | build: | ||
| 21 | cargo check --workspace --all-targets | ||
| 22 | |||
| 23 | # Lint with clippy, denying all warnings. | ||
| 24 | clippy: | ||
| 25 | cargo clippy --workspace --all-targets -- -D warnings | ||
| 26 | |||
| 27 | # Run the test suite. | ||
| 28 | test: | ||
| 29 | cargo test --workspace | ||
| 30 | |||
| 31 | # Run the full Nix gate (checks.{clippy,fmt,test,deny,doc}). | ||
| 32 | flake-check: | ||
| 33 | nix flake check | ||
rust-toolchain.toml +6 −0
| @@ -0,0 +1,6 @@ | |||
| 1 | # Nightly is a currency preference only: no `#![feature(...)]` gates, code must | ||
| 2 | # compile on stable. This file is for non-Nix users; Nix pins nightly via fenix | ||
| 3 | # through flake.lock. | ||
| 4 | [toolchain] | ||
| 5 | channel = "nightly" | ||
| 6 | components = ["rustfmt", "clippy", "rust-src", "rust-analyzer"] | ||
src/main.rs +14 −0
| @@ -0,0 +1,14 @@ | |||
| 1 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 4 | |||
| 5 | //! `fabrica` — a self-hosted git server. | ||
| 6 | //! | ||
| 7 | //! The binary is deliberately thin: it parses arguments and dispatches into the | ||
| 8 | //! [`cli`] crate, and nothing else lives here. | ||
| 9 | |||
| 10 | use std::process::ExitCode; | ||
| 11 | |||
| 12 | fn main() -> ExitCode { | ||
| 13 | cli::run() | ||
| 14 | } | ||