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 | + | } | |