commit e7fa12d6f3f5b723bce413acdfa5461fbaa3ec97 Author: atagen Date: Sat Jul 19 14:34:21 2025 +1000 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6f1297 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +assets/ +docs/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..baa49b9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,328 @@ +Mozilla Public License, version 2.0 + +1. Definitions + + 1.1. “Contributor” + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + + 1.2. “Contributor Version” + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + + 1.3. “Contribution” + means Covered Software of a particular Contributor. + + 1.4. “Covered Software” + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, + and Modifications of such Source Code Form, in each case + including portions thereof. + + 1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms + of a Secondary License. + + 1.6. “Executable Form” + means any form of the work other than Source Code Form. + + 1.7. “Larger Work” + means a work that combines Covered Software with other material, + in a separate file or files, that is not Covered Software. + + 1.8. “License” + means this document. + + 1.9. “Licensable” + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, + any and all of the rights conveyed by this License. + + 1.10. “Modifications” + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + + 1.11. “Patent Claims” of a Contributor + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + + 1.12. “Secondary License” + means either the GNU General Public License, Version 2.0, the + GNU Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those licenses. + + 1.13. “Source Code Form” + means the form of the work preferred for making modifications. + + 1.14. “You” (or “Your”) + means an individual or a legal entity exercising rights under this License. + For legal entities, “You” includes any entity that controls, + is controlled by, or is under common control with You. For purposes of + this definition, “control” means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by contract + or otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + +2. License Grants and Conditions + + 2.1. Grants + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, + or as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, + offer for sale, have made, import, and otherwise transfer either + its Contributions or its Contributor Version. + + 2.2. Effective Date + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor + first distributes such Contribution. + + 2.3. Limitations on Grant Scope + The licenses granted in this Section 2 are the only rights granted + under this License. No additional rights or licenses will be implied + from the distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted + by a Contributor: + + a. for any code that a Contributor has removed from + Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its + Contributor Version); or + + c. under Patent Claims infringed by Covered Software in the + absence of its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + + 2.4. Subsequent Licenses + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License + (if permitted under the terms of Section 3.3). + + 2.5. Representation + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights + to grant the rights to its Contributions conveyed by this License. + + 2.6. Fair Use + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, + or other equivalents. + + 2.7. Conditions + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the + licenses granted in Section 2.1. + +3. Responsibilities + + 3.1. Distribution of Source Form + All distribution of Covered Software in Source Code Form, including + any Modifications that You create or to which You contribute, must be + under the terms of this License. You must inform recipients that the + Source Code Form of the Covered Software is governed by the terms + of this License, and how they can obtain a copy of this License. + You may not attempt to alter or restrict the recipients’ rights + in the Source Code Form. + + 3.2. Distribution of Executable Form + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more than + the cost of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients’ rights in the Source Code Form under this License. + + 3.3. Distribution of a Larger Work + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of + Covered Software with a work governed by one or more Secondary Licenses, + and the Covered Software is not Incompatible With Secondary Licenses, + this License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the + Covered Software under the terms of either this License or such + Secondary License(s). + + 3.4. Notices + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, + or limitations of liability) contained within the Source Code Form of + the Covered Software, except that You may alter any license notices to + the extent required to remedy known factual inaccuracies. + + 3.5. Application of Additional Terms + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of + Covered Software. However, You may do so only on Your own behalf, + and not on behalf of any Contributor. You must make it absolutely clear + that any such warranty, support, indemnity, or liability obligation is + offered by You alone, and You hereby agree to indemnify every Contributor + for any liability incurred by such Contributor as a result of warranty, + support, indemnity or liability terms You offer. You may include + additional disclaimers of warranty and limitations of liability + specific to any jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + +If it is impossible for You to comply with any of the terms of this License +with respect to some or all of the Covered Software due to statute, +judicial order, or regulation then You must: (a) comply with the terms of +this License to the maximum extent possible; and (b) describe the limitations +and the code they affect. Such description must be placed in a text file +included with all distributions of the Covered Software under this License. +Except to the extent prohibited by statute or regulation, such description +must be sufficiently detailed for a recipient of ordinary skill +to be able to understand it. + +5. Termination + + 5.1. The rights granted under this License will terminate automatically + if You fail to comply with any of its terms. However, if You become + compliant, then the rights granted under this License from a particular + Contributor are reinstated (a) provisionally, unless and until such + Contributor explicitly and finally terminates Your grants, and (b) on an + ongoing basis, if such Contributor fails to notify You of the + non-compliance by some reasonable means prior to 60 days after You have + come back into compliance. Moreover, Your grants from a particular + Contributor are reinstated on an ongoing basis if such Contributor + notifies You of the non-compliance by some reasonable means, + this is the first time You have received notice of non-compliance with + this License from such Contributor, and You become compliant prior to + 30 days after Your receipt of the notice. + + 5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted + to You by any and all Contributors for the Covered Software under + Section 2.1 of this License shall terminate. + + 5.3. In the event of termination under Sections 5.1 or 5.2 above, all + end user license agreements (excluding distributors and resellers) which + have been validly granted by You or Your distributors under this License + prior to termination shall survive termination. + +6. Disclaimer of Warranty + +Covered Software is provided under this License on an “as is” basis, without +warranty of any kind, either expressed, implied, or statutory, including, +without limitation, warranties that the Covered Software is free of defects, +merchantable, fit for a particular purpose or non-infringing. The entire risk +as to the quality and performance of the Covered Software is with You. +Should any Covered Software prove defective in any respect, You +(not any Contributor) assume the cost of any necessary servicing, repair, +or correction. This disclaimer of warranty constitutes an essential part of +this License. No use of any Covered Software is authorized under this +License except under this disclaimer. + +7. Limitation of Liability + +Under no circumstances and under no legal theory, whether tort +(including negligence), contract, or otherwise, shall any Contributor, or +anyone who distributes Covered Software as permitted above, be liable to +You for any direct, indirect, special, incidental, or consequential damages +of any character including, without limitation, damages for lost profits, +loss of goodwill, work stoppage, computer failure or malfunction, or any and +all other commercial damages or losses, even if such party shall have been +informed of the possibility of such damages. This limitation of liability +shall not apply to liability for death or personal injury resulting from +such party’s negligence to the extent applicable law prohibits such +limitation. Some jurisdictions do not allow the exclusion or limitation of +incidental or consequential damages, so this exclusion and limitation may +not apply to You. + +8. Litigation + +Any litigation relating to this License may be brought only in the courts of +a jurisdiction where the defendant maintains its principal place of business +and such litigation shall be governed by laws of that jurisdiction, without +reference to its conflict-of-law provisions. Nothing in this Section shall +prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + +This License represents the complete agreement concerning the subject matter +hereof. If any provision of this License is held to be unenforceable, +such provision shall be reformed only to the extent necessary to make it +enforceable. Any law or regulation which provides that the language of a +contract shall be construed against the drafter shall not be used to construe +this License against a Contributor. + +10. Versions of the License + + 10.1. New Versions + Mozilla Foundation is the license steward. Except as provided in + Section 10.3, no one other than the license steward has the right to + modify or publish new versions of this License. Each version will be + given a distinguishing version number. + + 10.2. Effect of New Versions + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published + by the license steward. + + 10.3. Modified Versions + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + + 10.4. Distributing Source Code Form that is + Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this + License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the terms of the + Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed + with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to +look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible With Secondary Licenses”, + as defined by the Mozilla Public License, v. 2.0. diff --git a/README.md b/README.md new file mode 100644 index 0000000..25a4c51 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ + +# welcome to arbys + +![image](https://docs.atagen.co/arbys/assets/logo.png "Arby's logo") + +## we have the meats + +**Arb**itrar**y** **s**ymlink manager is a NixOS module designed to allow you to link anything - from anywhere, to anywhere. + +Reproducibility vs mutability is up to you; source from the nix store, a stow repo you manage yourself, or an arbitrary set of files on disk. + +However you choose to satisfy your slow-roasted meat cravings, Arbys guarantees that the set of links generated is consistent. + +## double beef 'n cheddar + +The Arbys interface is simple, and resembles hjem or home manager's files implementation. + +It can be as easy as: +````nix +{ ... }: +{ + environment = { + arbys.enable = true; + files."/var/anywhere".text = "It's tender, juicy deliciousness on the inside and perfectly fried for a satisfying crunch on the outside."; + }; +} +```` + +You can find the full documentation [here](https://docs.atagen.co/arbys). + +## smokehouse brisket + +Adding Arbys to your config is straightforward: +````nix +# flake.nix +{ + # first, add to your flake inputs + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + arbys.url = "git+https://git.atagen.co/atagen/arbys"; + }; + + # next, utilise the supplied NixOS module + outputs = { self, nixpkgs, arbys, ... }: + { + nixosConfigurations.mycoolhost = nixpkgs.lib.nixosSystem { + # fluff elided + modules = [ + arbys.nixosModules.arbys + ]; + }; + }; +} +```` +Then set up your linkages per the documentation above. + +Enjoy ! + +## classic french dip & swiss + +Arbys would not be possible without the hard work of the [feel-co](https://github.com/feel-co) team and their excellent [hjem](https://github.com/feel-co/hjem) and [smfh](https://github.com/feel-co/smfh) projects. + diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..5999137 --- /dev/null +++ b/flake.lock @@ -0,0 +1,7 @@ +{ + "nodes": { + "root": {} + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..43c5853 --- /dev/null +++ b/flake.nix @@ -0,0 +1,258 @@ +{ + # inputs = { + # nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + # unf = { + # url = "git+https://git.atagen.co/atagen/unf"; + # inputs.nixpkgs.follows = "nixpkgs"; + # }; + # }; + + outputs = + { + self, + # nixpkgs, + # unf, + }: + { + # uncomment for doc gen + # packages.x86_64-linux.docs = + # let + # pkgs = nixpkgs.legacyPackages.x86_64-linux; + # in + # pkgs.callPackage unf.lib.pak-chooie { + # inherit self; + # projectName = "arbys"; + # newPath = "https://git.atagen.co/atagen/arbys"; + # modules = [ self.nixosModules.arbys ]; + # }; + nixosModules.arbys = + { + pkgs, + lib, + config, + ... + }: + let + inherit (lib) + mkEnableOption + mkOption + mkIf + ; + inherit (lib.types) + submodule + bool + nullOr + lines + path + int + attrsOf + ; + fileType = submodule ( + { options, ... }: + { + options = { + enable = mkEnableOption "creation of this file" // { + default = true; + example = false; + }; + + text = mkOption { + default = ""; + type = lines; + description = "Text of the file"; + }; + + source = mkOption { + type = nullOr path; + default = null; + description = "Path of the source file or directory"; + }; + + executable = mkOption { + type = bool; + default = false; + example = true; + description = '' + Whether to set the execute bit on the target file. + Only effective in combination with `.text` specified, else permissions will be of the source file. + ''; + }; + + uid = mkOption { + type = nullOr int; + default = null; + description = '' + Uid this file will belong to + ''; + }; + + gid = mkOption { + type = nullOr int; + default = null; + description = '' + Gid this file will belong to + ''; + }; + + clobber = mkOption { + default = config.environment.arbys.clobber; + defaultText = ''config.environment.arbys.clobber''; + type = bool; + description = '' + Whether to clobber file or throw error on conflict + ''; + }; + + }; + } + ); + in + { + options.environment = { + arbys = { + clobber = mkOption { + type = bool; + default = false; + description = '' + Whether to clobber conflicted files by default + ''; + }; + enable = mkEnableOption "Arbitrary Symlink Manager"; + }; + files = mkOption { + description = "Files to link"; + type = attrsOf fileType; + default = { }; + }; + }; + config = + let + files = + let + enabled = lib.filterAttrs (_: v: v.enable) config.environment.files; + sourced = builtins.mapAttrs ( + n: v: + let + processed = + if (v.source == null) then + ( + if (v.text == "") then + throw "Must provide source or text for ${n}" + else + v + // { + source = pkgs.writeTextFile { + name = "${n}-source"; + text = v.text; + executable = v.executable ? false; + }; + } + ) + else + v; + filtered = builtins.removeAttrs processed [ + "text" + "enable" + "executable" + ]; + nullFiltered = lib.filterAttrs (_: v: v != null) filtered; + in + nullFiltered + // { + type = "symlink"; + } + ) enabled; + in + lib.foldlAttrs ( + acc: n: v: + acc ++ (lib.singleton ({ target = n; } // v)) + ) [ ] sourced; + manifest = pkgs.writeTextFile { + name = "manifest.json"; + text = ( + builtins.toJSON { + inherit files; + clobber_by_default = config.environment.arbys.clobber; + version = 1; + } + ); + checkPhase = '' + set -e + CUE_CACHE_DIR=$(pwd)/.cache + CUE_CONFIG_DIR=$(pwd)/.config + + ${lib.getExe pkgs.cue} vet -c ${./v1.cue} $target + ''; + }; + in + mkIf (config.environment.arbys.enable) { + systemd.targets.arbys = { + description = "Create Arbitrary Symlinks"; + requiredBy = [ "sysinit-reactivation.target" ]; + before = [ "sysinit-reactivation.target" ]; + requires = [ + "arbys-activate.service" + "arbys-copy.service" + "arbys-cleanup.service" + ]; + }; + + systemd.services = + let + manifestPath = "/var/lib/arbys"; + in + { + arbys-prep = { + description = "Ensure manifest dir exists"; + script = "mkdir -p ${manifestPath}"; + serviceConfig.Type = "oneshot"; + unitConfig.RefuseManualStart = true; + }; + + arbys-activate = { + description = "Link files from arbys manifest"; + serviceConfig.Type = "oneshot"; + requires = [ + "arbys-prep.service" + "arbys-copy.service" + ]; + after = [ "arbys-prep.service" ]; + script = + let + linker = lib.getExe pkgs.smfh; + in + '' + new_manifest=${manifest} + + if [ ! -f ${manifestPath}/manifest.json ]; then + ${linker} activate $new_manifest + exit 0 + fi; + + ${linker} diff $new_manifest ${manifestPath}/manifest.json + ''; + }; + + arbys-copy = { + description = "Copy manifest into the state dir"; + serviceConfig.Type = "oneshot"; + after = [ "arbys-activate.service" ]; + script = "cp ${manifest} ${manifestPath}/manifest.json"; + }; + + arbys-cleanup = { + description = "Clean up manifest if disabled"; + serviceConfig.type = "oneshot"; + after = [ "arbys.target" ]; + unitConfig.RefuseManualStart = false; + script = + if (lib.count (builtins.attrNames config.environment.files) == 0) then + "rm ${manifestPath}/manifest.json" + else + "true"; + }; + }; + }; + }; + }; +} diff --git a/v1.cue b/v1.cue new file mode 100644 index 0000000..ea1cdc7 --- /dev/null +++ b/v1.cue @@ -0,0 +1,31 @@ +#path_string: string & =~ "^/" +#file_string: #path_string & =~ "[^/]$" +#octal_string: string & =~ "^[0-7]{3,4}$" + +#ArbysFile: { + type!: string + source?: #path_string + target!: #path_string + clobber?: bool + permissions?: #octal_string + uid?: int & >=0 + gid?: int & >=0 + deactivate?: bool +} & ({ + type: "copy" + target!: #file_string + source!: #file_string + ... +} | { + type: "symlink" + source!: _ + ... +} | { + type: "delete" | "directory" | "modify" + ... +}) + +{ + version: 1 + files: [...#ArbysFile] +}