feat: namespace arg, inputs for packages and overlays, get lib from root, add checks
This commit is contained in:
parent
d6b766939a
commit
b2e6364075
13 changed files with 112 additions and 38 deletions
|
|
@ -62,7 +62,7 @@
|
||||||
aarch64-darwin = inputs.nixpkgs.legacyPackages.aarch64-darwin.alejandra;
|
aarch64-darwin = inputs.nixpkgs.legacyPackages.aarch64-darwin.alejandra;
|
||||||
};
|
};
|
||||||
|
|
||||||
_snowfall = rec {
|
snowfall = rec {
|
||||||
raw-config = config;
|
raw-config = config;
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
|
|
|
||||||
56
snowfall-lib/checks/default.nix
Normal file
56
snowfall-lib/checks/default.nix
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
core-inputs,
|
||||||
|
user-inputs,
|
||||||
|
snowfall-lib,
|
||||||
|
snowfall-config,
|
||||||
|
}: let
|
||||||
|
inherit (core-inputs.flake-utils-plus.lib) filterPackages;
|
||||||
|
inherit (core-inputs.nixpkgs.lib) assertMsg foldl mapAttrs callPackageWith;
|
||||||
|
|
||||||
|
user-checks-root = snowfall-lib.fs.get-snowfall-file "checks";
|
||||||
|
in {
|
||||||
|
check = {
|
||||||
|
## Create flake output packages.
|
||||||
|
## Example Usage:
|
||||||
|
## ```nix
|
||||||
|
## create-checks { inherit channels; src = ./my-checks; overrides = { inherit another-check; }; alias = { default = "another-check"; }; }
|
||||||
|
## ```
|
||||||
|
## Result:
|
||||||
|
## ```nix
|
||||||
|
## { another-check = ...; my-check = ...; default = ...; }
|
||||||
|
## ```
|
||||||
|
#@ Attrs -> Attrs
|
||||||
|
create-checks = {
|
||||||
|
channels,
|
||||||
|
src ? user-checks-root,
|
||||||
|
pkgs ? channels.nixpkgs,
|
||||||
|
overrides ? {},
|
||||||
|
alias ? {},
|
||||||
|
}: let
|
||||||
|
user-checks = snowfall-lib.fs.get-default-nix-files-recursive src;
|
||||||
|
create-check-metadata = check: let
|
||||||
|
extra-inputs =
|
||||||
|
pkgs
|
||||||
|
// {
|
||||||
|
inherit channels;
|
||||||
|
lib = snowfall-lib.internal.system-lib;
|
||||||
|
inputs = snowfall-lib.flake.without-src user-inputs;
|
||||||
|
namespace = snowfall-config.namespace;
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
name = builtins.unsafeDiscardStringContext (snowfall-lib.path.get-parent-directory check);
|
||||||
|
drv = callPackageWith extra-inputs check {};
|
||||||
|
};
|
||||||
|
checks-metadata = builtins.map create-check-metadata user-checks;
|
||||||
|
merge-checks = checks: metadata:
|
||||||
|
checks
|
||||||
|
// {
|
||||||
|
${metadata.name} = metadata.drv;
|
||||||
|
};
|
||||||
|
checks-without-aliases = foldl merge-checks {} checks-metadata;
|
||||||
|
aliased-checks = mapAttrs (name: value: checks-without-aliases.${value}) alias;
|
||||||
|
checks = checks-without-aliases // aliased-checks // overrides;
|
||||||
|
in
|
||||||
|
filterPackages pkgs.system checks;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -99,6 +99,7 @@ core-inputs: user-options: let
|
||||||
attrs = {
|
attrs = {
|
||||||
inherit (user-options) inputs;
|
inherit (user-options) inputs;
|
||||||
snowfall-inputs = core-inputs;
|
snowfall-inputs = core-inputs;
|
||||||
|
namespace = snowfall-config.namespace;
|
||||||
lib = merge-shallow [base-lib {${snowfall-config.namespace} = user-lib;}];
|
lib = merge-shallow [base-lib {${snowfall-config.namespace} = user-lib;}];
|
||||||
};
|
};
|
||||||
libs =
|
libs =
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ in rec {
|
||||||
"homes"
|
"homes"
|
||||||
"channels-config"
|
"channels-config"
|
||||||
"templates"
|
"templates"
|
||||||
"package-namespace"
|
"checks"
|
||||||
"alias"
|
"alias"
|
||||||
"snowfall"
|
"snowfall"
|
||||||
];
|
];
|
||||||
|
|
@ -96,7 +96,7 @@ in rec {
|
||||||
};
|
};
|
||||||
|
|
||||||
mkFlake = full-flake-options: let
|
mkFlake = full-flake-options: let
|
||||||
package-namespace = full-flake-options.package-namespace or snowfall-config.namespace or "internal";
|
namespace = snowfall-config.namespace or "internal";
|
||||||
custom-flake-options = flake.without-snowfall-options full-flake-options;
|
custom-flake-options = flake.without-snowfall-options full-flake-options;
|
||||||
alias = full-flake-options.alias or {};
|
alias = full-flake-options.alias or {};
|
||||||
homes = snowfall-lib.home.create-homes (full-flake-options.homes or {});
|
homes = snowfall-lib.home.create-homes (full-flake-options.homes or {});
|
||||||
|
|
@ -125,7 +125,7 @@ in rec {
|
||||||
alias = alias.modules.home or {};
|
alias = alias.modules.home or {};
|
||||||
};
|
};
|
||||||
overlays = snowfall-lib.overlay.create-overlays {
|
overlays = snowfall-lib.overlay.create-overlays {
|
||||||
inherit package-namespace;
|
inherit namespace;
|
||||||
extra-overlays = full-flake-options.extra-exported-overlays or {};
|
extra-overlays = full-flake-options.extra-exported-overlays or {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -136,7 +136,7 @@ in rec {
|
||||||
or (const {});
|
or (const {});
|
||||||
user-outputs = user-outputs-builder channels;
|
user-outputs = user-outputs-builder channels;
|
||||||
packages = snowfall-lib.package.create-packages {
|
packages = snowfall-lib.package.create-packages {
|
||||||
inherit channels package-namespace;
|
inherit channels namespace;
|
||||||
overrides = (full-flake-options.packages or {}) // (user-outputs.packages or {});
|
overrides = (full-flake-options.packages or {}) // (user-outputs.packages or {});
|
||||||
alias = alias.packages or {};
|
alias = alias.packages or {};
|
||||||
};
|
};
|
||||||
|
|
@ -145,9 +145,14 @@ in rec {
|
||||||
overrides = (full-flake-options.shells or {}) // (user-outputs.devShells or {});
|
overrides = (full-flake-options.shells or {}) // (user-outputs.devShells or {});
|
||||||
alias = alias.shells or {};
|
alias = alias.shells or {};
|
||||||
};
|
};
|
||||||
|
checks = snowfall-lib.check.create-checks {
|
||||||
|
inherit channels;
|
||||||
|
overrides = (full-flake-options.checks or {}) // (user-outputs.checks or {});
|
||||||
|
alias = alias.checks or {};
|
||||||
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
inherit packages;
|
inherit packages checks;
|
||||||
|
|
||||||
devShells = shells;
|
devShells = shells;
|
||||||
};
|
};
|
||||||
|
|
@ -170,13 +175,13 @@ in rec {
|
||||||
channelsConfig = full-flake-options.channels-config or {};
|
channelsConfig = full-flake-options.channels-config or {};
|
||||||
|
|
||||||
channels.nixpkgs.overlaysBuilder = snowfall-lib.overlay.create-overlays-builder {
|
channels.nixpkgs.overlaysBuilder = snowfall-lib.overlay.create-overlays-builder {
|
||||||
inherit package-namespace;
|
inherit namespace;
|
||||||
extra-overlays = full-flake-options.overlays or [];
|
extra-overlays = full-flake-options.overlays or [];
|
||||||
};
|
};
|
||||||
|
|
||||||
outputsBuilder = outputs-builder;
|
outputsBuilder = outputs-builder;
|
||||||
|
|
||||||
_snowfall = {
|
snowfall = {
|
||||||
config = snowfall-config;
|
config = snowfall-config;
|
||||||
raw-config = full-flake-options.snowfall or {};
|
raw-config = full-flake-options.snowfall or {};
|
||||||
user-lib = snowfall-lib.internal.user-lib;
|
user-lib = snowfall-lib.internal.user-lib;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ in {
|
||||||
## "/user-source/systems"
|
## "/user-source/systems"
|
||||||
## ```
|
## ```
|
||||||
#@ Path -> Path
|
#@ Path -> Path
|
||||||
get-file = path: "${user-inputs.src}/${path}";
|
get-file = path: user-inputs.src + "/${path}";
|
||||||
|
|
||||||
## Get a file path relative to the user's snowfall directory.
|
## Get a file path relative to the user's snowfall directory.
|
||||||
## Example Usage:
|
## Example Usage:
|
||||||
|
|
@ -48,7 +48,7 @@ in {
|
||||||
## "/user-source/snowfall-dir/systems"
|
## "/user-source/snowfall-dir/systems"
|
||||||
## ```
|
## ```
|
||||||
#@ Path -> Path
|
#@ Path -> Path
|
||||||
get-snowfall-file = path: "${snowfall-config.root}/${path}";
|
get-snowfall-file = path: snowfall-config.root + "/${path}";
|
||||||
|
|
||||||
## Get a file path relative to the this flake.
|
## Get a file path relative to the this flake.
|
||||||
## Example Usage:
|
## Example Usage:
|
||||||
|
|
@ -60,7 +60,7 @@ in {
|
||||||
## "/user-source/systems"
|
## "/user-source/systems"
|
||||||
## ```
|
## ```
|
||||||
#@ Path -> Path
|
#@ Path -> Path
|
||||||
internal-get-file = path: "${core-inputs.src}/${path}";
|
internal-get-file = path: core-inputs.src + "/${path}";
|
||||||
|
|
||||||
## Safely read from a directory if it exists.
|
## Safely read from a directory if it exists.
|
||||||
## Example Usage:
|
## Example Usage:
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@ in {
|
||||||
format = "home";
|
format = "home";
|
||||||
|
|
||||||
inputs = snowfall-lib.flake.without-src user-inputs;
|
inputs = snowfall-lib.flake.without-src user-inputs;
|
||||||
|
namespace = snowfall-config.namespace;
|
||||||
|
|
||||||
# NOTE: home-manager has trouble with `pkgs` recursion if it isn't passed in here.
|
# NOTE: home-manager has trouble with `pkgs` recursion if it isn't passed in here.
|
||||||
inherit pkgs lib;
|
inherit pkgs lib;
|
||||||
|
|
@ -345,8 +346,10 @@ in {
|
||||||
|
|
||||||
# NOTE: specialArgs are not propagated by Home-Manager without this.
|
# NOTE: specialArgs are not propagated by Home-Manager without this.
|
||||||
# However, not all specialArgs values can be set when using `_module.args`.
|
# However, not all specialArgs values can be set when using `_module.args`.
|
||||||
_module.args =
|
_module.args = builtins.removeAttrs ((users.users.${name}.specialArgs or {})
|
||||||
builtins.removeAttrs (users.users.${name}.specialArgs or {})
|
// {
|
||||||
|
namespace = snowfall-config.namespace;
|
||||||
|
})
|
||||||
["options" "config" "lib" "pkgs" "specialArgs" "host"];
|
["options" "config" "lib" "pkgs" "specialArgs" "host"];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
{snowfall = snowfall-lib;}
|
{snowfall = snowfall-lib;}
|
||||||
];
|
];
|
||||||
|
|
||||||
user-lib-root = snowfall-lib.fs.get-file "lib";
|
user-lib-root = snowfall-lib.fs.get-snowfall-file "lib";
|
||||||
user-lib-modules = snowfall-lib.fs.get-default-nix-files-recursive user-lib-root;
|
user-lib-modules = snowfall-lib.fs.get-default-nix-files-recursive user-lib-root;
|
||||||
|
|
||||||
user-lib = fix (
|
user-lib = fix (
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
attrs = {
|
attrs = {
|
||||||
inputs = snowfall-lib.flake.without-snowfall-inputs user-inputs;
|
inputs = snowfall-lib.flake.without-snowfall-inputs user-inputs;
|
||||||
snowfall-inputs = core-inputs;
|
snowfall-inputs = core-inputs;
|
||||||
|
namespace = snowfall-config.namespace;
|
||||||
lib = snowfall-lib.attrs.merge-shallow [
|
lib = snowfall-lib.attrs.merge-shallow [
|
||||||
base-lib
|
base-lib
|
||||||
{internal = user-lib;}
|
{internal = user-lib;}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ in {
|
||||||
pkgs = user-inputs.self.pkgs.${system}.nixpkgs;
|
pkgs = user-inputs.self.pkgs.${system}.nixpkgs;
|
||||||
|
|
||||||
inputs = snowfall-lib.flake.without-src user-inputs;
|
inputs = snowfall-lib.flake.without-src user-inputs;
|
||||||
|
namespace = snowfall-config.namespace;
|
||||||
};
|
};
|
||||||
imported-user-module = import metadata.path;
|
imported-user-module = import metadata.path;
|
||||||
user-module =
|
user-module =
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ in {
|
||||||
## Create a flake-utils-plus overlays builder.
|
## Create a flake-utils-plus overlays builder.
|
||||||
## Example Usage:
|
## Example Usage:
|
||||||
## ```nix
|
## ```nix
|
||||||
## create-overlays { src = ./my-overlays; package-namespace = "my-packages"; }
|
## create-overlays { src = ./my-overlays; namespace = "my-packages"; }
|
||||||
## ```
|
## ```
|
||||||
## Result:
|
## Result:
|
||||||
## ```nix
|
## ```nix
|
||||||
|
|
@ -22,7 +22,7 @@ in {
|
||||||
#@ Attrs -> Attrs -> [(a -> b -> c)]
|
#@ Attrs -> Attrs -> [(a -> b -> c)]
|
||||||
create-overlays-builder = {
|
create-overlays-builder = {
|
||||||
src ? user-overlays-root,
|
src ? user-overlays-root,
|
||||||
package-namespace ? "internal",
|
namespace ? snowfall-config.namespace,
|
||||||
extra-overlays ? [],
|
extra-overlays ? [],
|
||||||
}: channels: let
|
}: channels: let
|
||||||
user-overlays = snowfall-lib.fs.get-default-nix-files-recursive src;
|
user-overlays = snowfall-lib.fs.get-default-nix-files-recursive src;
|
||||||
|
|
@ -39,11 +39,11 @@ in {
|
||||||
user-packages-overlay = final: prev: let
|
user-packages-overlay = final: prev: let
|
||||||
user-packages = snowfall-lib.package.create-packages {
|
user-packages = snowfall-lib.package.create-packages {
|
||||||
pkgs = final;
|
pkgs = final;
|
||||||
channels = channels;
|
inherit channels namespace;
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
${package-namespace} =
|
${namespace} =
|
||||||
(prev.${package-namespace} or {})
|
(prev.${namespace} or {})
|
||||||
// user-packages;
|
// user-packages;
|
||||||
};
|
};
|
||||||
overlays =
|
overlays =
|
||||||
|
|
@ -55,7 +55,7 @@ in {
|
||||||
##
|
##
|
||||||
## Example Usage:
|
## Example Usage:
|
||||||
## ```nix
|
## ```nix
|
||||||
## create-overlays { src = ./my-overlays; packages-src = ./my-packages; package-namespace = "my-namespace"; extra-overlays = {}; }
|
## create-overlays { src = ./my-overlays; packages-src = ./my-packages; namespace = "my-namespace"; extra-overlays = {}; }
|
||||||
## ```
|
## ```
|
||||||
## Result:
|
## Result:
|
||||||
## ```nix
|
## ```nix
|
||||||
|
|
@ -65,7 +65,7 @@ in {
|
||||||
create-overlays = {
|
create-overlays = {
|
||||||
src ? user-overlays-root,
|
src ? user-overlays-root,
|
||||||
packages-src ? user-packages-root,
|
packages-src ? user-packages-root,
|
||||||
package-namespace ? null,
|
namespace ? snowfall-config.namespace,
|
||||||
extra-overlays ? {},
|
extra-overlays ? {},
|
||||||
}: let
|
}: let
|
||||||
fake-pkgs = {
|
fake-pkgs = {
|
||||||
|
|
@ -83,13 +83,14 @@ in {
|
||||||
user-packages = snowfall-lib.package.create-packages {
|
user-packages = snowfall-lib.package.create-packages {
|
||||||
pkgs = final;
|
pkgs = final;
|
||||||
channels = channel-systems.${prev.system};
|
channels = channel-systems.${prev.system};
|
||||||
|
inherit namespace;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
if package-namespace == null
|
if namespace == null
|
||||||
then user-packages
|
then user-packages
|
||||||
else {
|
else {
|
||||||
${package-namespace} =
|
${namespace} =
|
||||||
(prev.${package-namespace} or {})
|
(prev.${namespace} or {})
|
||||||
// user-packages;
|
// user-packages;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -102,21 +103,21 @@ in {
|
||||||
# Deprecated: Use `inputs.*` instead of referencing the input name directly.
|
# Deprecated: Use `inputs.*` instead of referencing the input name directly.
|
||||||
user-inputs
|
user-inputs
|
||||||
// {
|
// {
|
||||||
inherit channels;
|
inherit channels namespace;
|
||||||
inputs = user-inputs;
|
inputs = user-inputs;
|
||||||
lib = snowfall-lib.internal.system-lib;
|
lib = snowfall-lib.internal.system-lib;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
packages = user-packages-overlay final prev;
|
packages = user-packages-overlay final prev;
|
||||||
prev-with-packages =
|
prev-with-packages =
|
||||||
if package-namespace == null
|
if namespace == null
|
||||||
then prev // packages
|
then prev // packages
|
||||||
else
|
else
|
||||||
prev
|
prev
|
||||||
// {
|
// {
|
||||||
${package-namespace} =
|
${namespace} =
|
||||||
(prev.${package-namespace} or {})
|
(prev.${namespace} or {})
|
||||||
// packages.${package-namespace};
|
// packages.${namespace};
|
||||||
};
|
};
|
||||||
user-overlay-packages =
|
user-overlay-packages =
|
||||||
user-overlay
|
user-overlay
|
||||||
|
|
@ -152,14 +153,15 @@ in {
|
||||||
overlay = final: prev: let
|
overlay = final: prev: let
|
||||||
channels = channel-systems.${prev.system};
|
channels = channel-systems.${prev.system};
|
||||||
packages = snowfall-lib.package.create-packages {
|
packages = snowfall-lib.package.create-packages {
|
||||||
|
inherit namespace;
|
||||||
channels = channel-systems.${prev.system};
|
channels = channel-systems.${prev.system};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
if package-namespace == null
|
if namespace == null
|
||||||
then {${name} = packages.${name};}
|
then {${name} = packages.${name};}
|
||||||
else {
|
else {
|
||||||
${package-namespace} =
|
${namespace} =
|
||||||
(prev.${package-namespace} or {})
|
(prev.${namespace} or {})
|
||||||
// {${name} = packages.${name};};
|
// {${name} = packages.${name};};
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,21 @@ in {
|
||||||
pkgs ? channels.nixpkgs,
|
pkgs ? channels.nixpkgs,
|
||||||
overrides ? {},
|
overrides ? {},
|
||||||
alias ? {},
|
alias ? {},
|
||||||
package-namespace ? "internal",
|
namespace ? snowfall-config.namespace,
|
||||||
}: let
|
}: let
|
||||||
user-packages = snowfall-lib.fs.get-default-nix-files-recursive src;
|
user-packages = snowfall-lib.fs.get-default-nix-files-recursive src;
|
||||||
create-package-metadata = package: let
|
create-package-metadata = package: let
|
||||||
namespaced-packages = {
|
namespaced-packages = {
|
||||||
${package-namespace} = packages-without-aliases;
|
${namespace} = packages-without-aliases;
|
||||||
};
|
};
|
||||||
extra-inputs =
|
extra-inputs =
|
||||||
pkgs
|
pkgs
|
||||||
// namespaced-packages
|
// namespaced-packages
|
||||||
// {
|
// {
|
||||||
inherit channels;
|
inherit channels namespace;
|
||||||
lib = snowfall-lib.internal.system-lib;
|
lib = snowfall-lib.internal.system-lib;
|
||||||
pkgs = pkgs // namespaced-packages;
|
pkgs = pkgs // namespaced-packages;
|
||||||
inputs = snowfall-lib.flake.without-snowfall-inputs user-inputs;
|
inputs = user-inputs;
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
name = builtins.unsafeDiscardStringContext (snowfall-lib.path.get-parent-directory package);
|
name = builtins.unsafeDiscardStringContext (snowfall-lib.path.get-parent-directory package);
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ in {
|
||||||
inherit channels;
|
inherit channels;
|
||||||
lib = snowfall-lib.internal.system-lib;
|
lib = snowfall-lib.internal.system-lib;
|
||||||
inputs = snowfall-lib.flake.without-src user-inputs;
|
inputs = snowfall-lib.flake.without-src user-inputs;
|
||||||
|
namespace = snowfall-config.namespace;
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
name = builtins.unsafeDiscardStringContext (snowfall-lib.path.get-parent-directory shell);
|
name = builtins.unsafeDiscardStringContext (snowfall-lib.path.get-parent-directory shell);
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,7 @@ in {
|
||||||
|
|
||||||
virtual = (get-virtual-system-type target) != "";
|
virtual = (get-virtual-system-type target) != "";
|
||||||
inputs = snowfall-lib.flake.without-src user-inputs;
|
inputs = snowfall-lib.flake.without-src user-inputs;
|
||||||
|
namespace = snowfall-config.namespace;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ in {
|
||||||
templates
|
templates
|
||||||
// {
|
// {
|
||||||
${metadata.name} =
|
${metadata.name} =
|
||||||
|
(builtins.trace "name: ${metadata.name}")
|
||||||
|
(builtins.trace "path: ${metadata.path}")
|
||||||
(overrides.${metadata.name} or {})
|
(overrides.${metadata.name} or {})
|
||||||
// {
|
// {
|
||||||
inherit (metadata) path;
|
inherit (metadata) path;
|
||||||
|
|
@ -44,7 +46,8 @@ in {
|
||||||
};
|
};
|
||||||
templates-without-aliases = foldl merge-templates {} templates-metadata;
|
templates-without-aliases = foldl merge-templates {} templates-metadata;
|
||||||
aliased-templates = mapAttrs (name: value: templates-without-aliases.${value}) alias;
|
aliased-templates = mapAttrs (name: value: templates-without-aliases.${value}) alias;
|
||||||
templates = templates-without-aliases // aliased-templates // overrides;
|
unused-overrides = builtins.removeAttrs overrides (builtins.map (metadata: metadata.name) templates-metadata);
|
||||||
|
templates = templates-without-aliases // aliased-templates // unused-overrides;
|
||||||
in
|
in
|
||||||
templates;
|
templates;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue