mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-04-16 08:48:09 +00:00
stash: refactor; 0.25.1 -> 0.27.2; nixos/stash: init (#323231)
This commit is contained in:
commit
2ab9225a40
@ -6173,6 +6173,13 @@
|
||||
githubId = 20759788;
|
||||
name = "JP Lippold";
|
||||
};
|
||||
DrakeTDL = {
|
||||
name = "Drake";
|
||||
email = "draketdl@mailbox.org";
|
||||
matrix = "@draketdl:matrix.org";
|
||||
github = "DrakeTDL";
|
||||
githubId = 22124013;
|
||||
};
|
||||
dramaturg = {
|
||||
email = "seb@ds.ag";
|
||||
github = "dramaturg";
|
||||
|
@ -115,6 +115,8 @@
|
||||
|
||||
- [Zipline](https://zipline.diced.sh/), a ShareX/file upload server that is easy to use, packed with features, and with an easy setup. Available as [services.zipline](#opt-services.zipline.enable).
|
||||
|
||||
- [Stash](https://github.com/stashapp/stash), An organizer for your adult videos/images, written in Go. Available as [services.stash](#opt-services.stash.enable).
|
||||
|
||||
- [Fider](https://fider.io/), an open platform to collect and prioritize feedback. Available as [services.fider](#opt-services.fider.enable).
|
||||
|
||||
- [mqtt-exporter](https://github.com/kpetremann/mqtt-exporter/), a Prometheus exporter for exposing messages from MQTT. Available as [services.prometheus.exporters.mqtt](#opt-services.prometheus.exporters.mqtt.enable).
|
||||
|
@ -1577,6 +1577,7 @@
|
||||
./services/web-apps/snipe-it.nix
|
||||
./services/web-apps/sogo.nix
|
||||
./services/web-apps/stirling-pdf.nix
|
||||
./services/web-apps/stash.nix
|
||||
./services/web-apps/trilium.nix
|
||||
./services/web-apps/tt-rss.nix
|
||||
./services/web-apps/vikunja.nix
|
||||
|
585
nixos/modules/services/web-apps/stash.nix
Normal file
585
nixos/modules/services/web-apps/stash.nix
Normal file
@ -0,0 +1,585 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib)
|
||||
getExe
|
||||
literalExpression
|
||||
mkEnableOption
|
||||
mkIf
|
||||
mkOption
|
||||
mkPackageOption
|
||||
optionalString
|
||||
toUpper
|
||||
types
|
||||
;
|
||||
|
||||
cfg = config.services.stash;
|
||||
|
||||
stashType = types.submodule {
|
||||
options = {
|
||||
path = mkOption {
|
||||
type = types.path;
|
||||
description = "location of your media files";
|
||||
};
|
||||
excludevideo = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to exclude video files from being scanned into Stash";
|
||||
};
|
||||
excludeimage = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to exclude image files from being scanned into Stash";
|
||||
};
|
||||
};
|
||||
};
|
||||
stashBoxType = types.submodule {
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
description = "The name of the Stash Box";
|
||||
};
|
||||
endpoint = mkOption {
|
||||
type = types.str;
|
||||
description = "URL to the Stash Box graphql api";
|
||||
};
|
||||
apikey = mkOption {
|
||||
type = types.str;
|
||||
description = "Stash Box API key";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
recentlyReleased = mode: {
|
||||
__typename = "CustomFilter";
|
||||
message = {
|
||||
id = "recently_released_objects";
|
||||
values.objects = mode;
|
||||
};
|
||||
mode = toUpper mode;
|
||||
sortBy = "date";
|
||||
direction = "DESC";
|
||||
};
|
||||
recentlyAdded = mode: {
|
||||
__typename = "CustomFilter";
|
||||
message = {
|
||||
id = "recently_added_objects";
|
||||
values.objects = mode;
|
||||
};
|
||||
mode = toUpper mode;
|
||||
sortBy = "created_at";
|
||||
direction = "DESC";
|
||||
};
|
||||
uiPresets = {
|
||||
recentlyReleasedScenes = recentlyReleased "Scenes";
|
||||
recentlyAddedScenes = recentlyAdded "Scenes";
|
||||
recentlyReleasedGalleries = recentlyReleased "Galleries";
|
||||
recentlyAddedGalleries = recentlyAdded "Galleries";
|
||||
recentlyAddedImages = recentlyAdded "Images";
|
||||
recentlyReleasedMovies = recentlyReleased "Movies";
|
||||
recentlyAddedMovies = recentlyAdded "Movies";
|
||||
recentlyAddedStudios = recentlyAdded "Studios";
|
||||
recentlyAddedPerformers = recentlyAdded "Performers";
|
||||
};
|
||||
|
||||
settingsFormat = pkgs.formats.yaml { };
|
||||
settingsFile = settingsFormat.generate "config.yml" cfg.settings;
|
||||
settingsType = types.submodule {
|
||||
freeformType = settingsFormat.type;
|
||||
|
||||
options = {
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
example = "::1";
|
||||
description = "The ip address that Stash should bind to.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 9999;
|
||||
example = 1234;
|
||||
description = "The port that Stash should listen on.";
|
||||
};
|
||||
|
||||
stash = mkOption {
|
||||
type = types.listOf stashType;
|
||||
description = ''
|
||||
Add directories containing your adult videos and images.
|
||||
Stash will use these directories to find videos and/or images during scanning.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
{
|
||||
stash = [
|
||||
{
|
||||
Path = "/media/drive/videos";
|
||||
ExcludeImage = true;
|
||||
}
|
||||
];
|
||||
}
|
||||
'';
|
||||
};
|
||||
stash_boxes = mkOption {
|
||||
type = types.listOf stashBoxType;
|
||||
default = [ ];
|
||||
description = ''Stash-box facilitates automated tagging of scenes and performers based on fingerprints and filenames'';
|
||||
example = literalExpression ''
|
||||
{
|
||||
stash_boxes = [
|
||||
{
|
||||
name = "StashDB";
|
||||
endpoint = "https://stashdb.org/graphql";
|
||||
apikey = "aaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbbb.cccccccccccccc";
|
||||
}
|
||||
];
|
||||
}
|
||||
'';
|
||||
};
|
||||
ui.frontPageContent = mkOption {
|
||||
description = "Search filters to display on the front page.";
|
||||
type = types.either (types.listOf types.attrs) (types.functionTo (types.listOf types.attrs));
|
||||
default = presets: [
|
||||
presets.recentlyReleasedScenes
|
||||
presets.recentlyAddedStudios
|
||||
presets.recentlyReleasedMovies
|
||||
presets.recentlyAddedPerformers
|
||||
presets.recentlyReleasedGalleries
|
||||
];
|
||||
example = literalExpression ''
|
||||
presets: [
|
||||
# To get the savedFilterId, you can query `{ findSavedFilters(mode: <FilterMode>) { id name } }` on localhost:9999/graphql
|
||||
{
|
||||
__typename = "SavedFilter";
|
||||
savedFilterId = 1;
|
||||
}
|
||||
# basic custom filter
|
||||
{
|
||||
__typename = "CustomFilter";
|
||||
title = "Random Scenes";
|
||||
mode = "SCENES";
|
||||
sortBy = "random";
|
||||
direction = "DESC";
|
||||
}
|
||||
presets.recentlyAddedImages
|
||||
]
|
||||
'';
|
||||
apply = type: if builtins.isFunction type then (type uiPresets) else type;
|
||||
};
|
||||
blobs_path = mkOption {
|
||||
type = types.path;
|
||||
default = "${cfg.dataDir}/blobs";
|
||||
description = "Path to blobs";
|
||||
};
|
||||
cache = mkOption {
|
||||
type = types.path;
|
||||
default = "${cfg.dataDir}/cache";
|
||||
description = "Path to cache";
|
||||
};
|
||||
database = mkOption {
|
||||
type = types.path;
|
||||
default = "${cfg.dataDir}/go.sqlite";
|
||||
description = "Path to the SQLite database";
|
||||
};
|
||||
generated = mkOption {
|
||||
type = types.path;
|
||||
default = "${cfg.dataDir}/generated";
|
||||
description = "Path to generated files";
|
||||
};
|
||||
plugins_path = mkOption {
|
||||
type = types.path;
|
||||
default = "${cfg.dataDir}/plugins";
|
||||
description = "Path to scrapers";
|
||||
};
|
||||
scrapers_path = mkOption {
|
||||
type = types.path;
|
||||
default = "${cfg.dataDir}/scrapers";
|
||||
description = "Path to scrapers";
|
||||
};
|
||||
|
||||
blobs_storage = mkOption {
|
||||
type = types.enum [
|
||||
"FILESYSTEM"
|
||||
"DATABASE"
|
||||
];
|
||||
default = "FILESYSTEM";
|
||||
description = "Where to store blobs";
|
||||
};
|
||||
calculate_md5 = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to calculate MD5 checksums for scene video files";
|
||||
};
|
||||
create_image_clip_from_videos = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Create Image Clips from Video extensions when Videos are disabled in Library";
|
||||
};
|
||||
dangerous_allow_public_without_auth = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Learn more at https://docs.stashapp.cc/networking/authentication-required-when-accessing-stash-from-the-internet/";
|
||||
};
|
||||
gallery_cover_regex = mkOption {
|
||||
type = types.str;
|
||||
default = "(poster|cover|folder|board)\.[^\.]+$";
|
||||
description = "Regex used to identify images as gallery covers";
|
||||
};
|
||||
no_proxy = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12";
|
||||
description = "A list of domains for which the proxy must not be used";
|
||||
};
|
||||
nobrowser = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "If we should not auto-open a browser window on startup";
|
||||
};
|
||||
notifications_enabled = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "If we should send notifications to the desktop";
|
||||
};
|
||||
parallel_tasks = mkOption {
|
||||
type = types.int;
|
||||
default = 1;
|
||||
description = "Number of parallel tasks to start during scan/generate";
|
||||
};
|
||||
preview_audio = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Include audio stream in previews";
|
||||
};
|
||||
preview_exclude_end = mkOption {
|
||||
type = types.int;
|
||||
default = 0;
|
||||
description = "Duration of start of video to exclude when generating previews";
|
||||
};
|
||||
preview_exclude_start = mkOption {
|
||||
type = types.int;
|
||||
default = 0;
|
||||
description = "Duration of end of video to exclude when generating previews";
|
||||
};
|
||||
preview_segment_duration = mkOption {
|
||||
type = types.float;
|
||||
default = 0.75;
|
||||
description = "Preview segment duration, in seconds";
|
||||
};
|
||||
preview_segments = mkOption {
|
||||
type = types.int;
|
||||
default = 12;
|
||||
description = "Number of segments in a preview file";
|
||||
};
|
||||
security_tripwire_accessed_from_public_internet = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "";
|
||||
description = "Learn more at https://docs.stashapp.cc/networking/authentication-required-when-accessing-stash-from-the-internet/";
|
||||
};
|
||||
sequential_scanning = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Modifies behaviour of the scanning functionality to generate support files (previews/sprites/phash) at the same time as fingerprinting/screenshotting";
|
||||
};
|
||||
show_one_time_moved_notification = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Whether a small notification to inform the user that Stash will no longer show a terminal window, and instead will be available in the tray";
|
||||
};
|
||||
sound_on_preview = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable sound on mouseover previews";
|
||||
};
|
||||
theme_color = mkOption {
|
||||
type = types.str;
|
||||
default = "#202b33";
|
||||
description = "Sets the `theme-color` property in the UI";
|
||||
};
|
||||
video_file_naming_algorithm = mkOption {
|
||||
type = types.enum [
|
||||
"OSHASH"
|
||||
"MD5"
|
||||
];
|
||||
default = "OSHASH";
|
||||
description = "Hash algorithm to use for generated file naming";
|
||||
};
|
||||
write_image_thumbnails = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Write image thumbnails to disk when generating on the fly";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pluginType =
|
||||
kind:
|
||||
mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [ ];
|
||||
description = ''
|
||||
The ${kind} Stash should be started with.
|
||||
'';
|
||||
apply =
|
||||
srcs:
|
||||
optionalString (srcs != [ ]) (
|
||||
pkgs.runCommand "stash-${kind}"
|
||||
{
|
||||
inherit srcs;
|
||||
nativeBuildInputs = [ pkgs.yq-go ];
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
''
|
||||
find $srcs -mindepth 1 -name '*.yml' | while read plugin_file; do
|
||||
grep -q "^#pkgignore" "$plugin_file" && continue
|
||||
|
||||
plugin_dir=$(dirname $plugin_file)
|
||||
out_path=$out/$(basename $plugin_dir)
|
||||
mkdir -p $out_path
|
||||
ls $plugin_dir | xargs -I{} ln -sf "$plugin_dir/{}" $out_path
|
||||
|
||||
env \
|
||||
plugin_id=$(basename $plugin_file .yml) \
|
||||
plugin_name="$(yq '.name' $plugin_file)" \
|
||||
plugin_description="$(yq '.description' $plugin_file)" \
|
||||
plugin_version="$(yq '.version' $plugin_file)" \
|
||||
plugin_files="$(find -L $out_path -mindepth 1 -type f -printf "%P\n")" \
|
||||
yq -n '
|
||||
.id = strenv(plugin_id) |
|
||||
.name = strenv(plugin_name) |
|
||||
(
|
||||
strenv(plugin_description) as $desc |
|
||||
with(select($desc == "null"); .metadata = {}) |
|
||||
with(select($desc != "null"); .metadata.description = $desc)
|
||||
) |
|
||||
(
|
||||
strenv(plugin_version) as $ver |
|
||||
with(select($ver == "null"); .version = "Unknown") |
|
||||
with(select($ver != "null"); .version = $ver)
|
||||
) |
|
||||
.date = (now | format_datetime("2006-01-02 15:04:05")) |
|
||||
.files = (strenv(plugin_files) | split("\n"))
|
||||
' > $out_path/manifest
|
||||
done
|
||||
''
|
||||
);
|
||||
};
|
||||
in
|
||||
{
|
||||
meta = {
|
||||
buildDocsInSandbox = false;
|
||||
maintainers = with lib.maintainers; [ DrakeTDL ];
|
||||
};
|
||||
|
||||
options = {
|
||||
services.stash = {
|
||||
enable = mkEnableOption "stash";
|
||||
|
||||
package = mkPackageOption pkgs "stash" { };
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "stash";
|
||||
description = "User under which Stash runs.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "stash";
|
||||
description = "Group under which Stash runs.";
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/stash";
|
||||
description = "The directory where Stash stores its files.";
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Open ports in the firewall for the Stash web interface.";
|
||||
};
|
||||
|
||||
username = mkOption {
|
||||
type = types.nullOr types.nonEmptyStr;
|
||||
default = null;
|
||||
example = "admin";
|
||||
description = ''
|
||||
Username for login.
|
||||
|
||||
::: {.warning}
|
||||
This option takes precedence over {option}`services.stash.settings.username`
|
||||
::
|
||||
|
||||
'';
|
||||
};
|
||||
|
||||
passwordFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/path/to/password/file";
|
||||
description = ''
|
||||
Path to file containing password for login.
|
||||
|
||||
::: {.warning}
|
||||
This option takes precedence over {option}`services.stash.settings.password`
|
||||
::
|
||||
|
||||
'';
|
||||
};
|
||||
|
||||
jwtSecretKeyFile = mkOption {
|
||||
type = types.path;
|
||||
description = "Path to file containing a secret used to sign JWT tokens.";
|
||||
};
|
||||
sessionStoreKeyFile = mkOption {
|
||||
type = types.path;
|
||||
description = "Path to file containing a secret for session store.";
|
||||
};
|
||||
|
||||
mutableSettings = mkOption {
|
||||
description = ''
|
||||
Whether the Stash config.yml is writeable by Stash.
|
||||
|
||||
If `false`, Any config changes done from within Stash UI will be temporary and reset to those defined in {option}`services.stash.settings` upon `Stash.service` restart.
|
||||
If `true`, the {option}`services.stash.settings` will only be used to initialize the Stash configuration if it does not exist, and are subsequently ignored.
|
||||
'';
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
mutablePlugins = mkEnableOption "Whether plugins/themes can be installed, updated, uninstalled manually.";
|
||||
mutableScrapers = mkEnableOption "Whether scrapers can be installed, updated, uninstalled manually.";
|
||||
plugins = pluginType "plugins";
|
||||
scrapers = pluginType "scrapers";
|
||||
|
||||
settings = mkOption {
|
||||
type = settingsType;
|
||||
description = "Stash configuration";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion =
|
||||
!lib.xor (cfg.username != null || cfg.settings.username or null != null) (
|
||||
cfg.passwordFile != null || cfg.settings.password or null != null
|
||||
);
|
||||
message = "You must set either both username and password, or neither.";
|
||||
}
|
||||
];
|
||||
|
||||
services.stash.settings = {
|
||||
username = mkIf (cfg.username != null) cfg.username;
|
||||
plugins_path = mkIf (!cfg.mutablePlugins) cfg.plugins;
|
||||
scrapers_path = mkIf (!cfg.mutableScrapers) cfg.scrapers;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.port ];
|
||||
|
||||
users.users.${cfg.user} = {
|
||||
inherit (cfg) group;
|
||||
isSystemUser = true;
|
||||
home = cfg.dataDir;
|
||||
};
|
||||
users.groups.${cfg.group} = { };
|
||||
|
||||
systemd = {
|
||||
tmpfiles.settings."10-stash-datadir".${cfg.dataDir}."d" = {
|
||||
inherit (cfg) user group;
|
||||
mode = "0755";
|
||||
};
|
||||
services.stash = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
path = with pkgs; [
|
||||
ffmpeg-full
|
||||
python3
|
||||
ruby
|
||||
];
|
||||
environment.STASH_CONFIG_FILE = "${cfg.dataDir}/config.yml";
|
||||
serviceConfig = {
|
||||
DynamicUser = false;
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
Restart = "on-failure";
|
||||
WorkingDirectory = cfg.dataDir;
|
||||
StateDirectory = mkIf (cfg.dataDir == "/var/lib/stash") (baseNameOf cfg.dataDir);
|
||||
ExecStartPre = pkgs.writers.writeBash "stash-setup.bash" (
|
||||
''
|
||||
install -d ${cfg.settings.generated}
|
||||
if [[ ! -z "${toString cfg.mutableSettings}" || ! -f ${cfg.dataDir}/config.yml ]]; then
|
||||
env \
|
||||
password=$(< ${cfg.passwordFile}) \
|
||||
jwtSecretKeyFile=$(< ${cfg.jwtSecretKeyFile}) \
|
||||
sessionStoreKeyFile=$(< ${cfg.sessionStoreKeyFile}) \
|
||||
${lib.getExe pkgs.yq-go} '
|
||||
.jwt_secret_key = strenv(jwtSecretKeyFile) |
|
||||
.session_store_key = strenv(sessionStoreKeyFile) |
|
||||
(
|
||||
strenv(password) as $password |
|
||||
with(select($password != ""); .password = $password)
|
||||
)
|
||||
' ${settingsFile} > ${cfg.dataDir}/config.yml
|
||||
fi
|
||||
''
|
||||
+ optionalString cfg.mutablePlugins ''
|
||||
install -d ${cfg.settings.plugins_path}
|
||||
ls ${cfg.plugins} | xargs -I{} ln -sf '${cfg.plugins}/{}' ${cfg.settings.plugins_path}
|
||||
''
|
||||
+ optionalString cfg.mutableScrapers ''
|
||||
install -d ${cfg.settings.scrapers_path}
|
||||
ls ${cfg.scrapers} | xargs -I{} ln -sf '${cfg.scrapers}/{}' ${cfg.settings.scrapers_path}
|
||||
''
|
||||
);
|
||||
ExecStart = getExe cfg.package;
|
||||
|
||||
ProtectHome = "tmpfs";
|
||||
BindReadOnlyPaths = mkIf (cfg.settings != { }) (map (stash: "${stash.path}") cfg.settings.stash);
|
||||
|
||||
# hardening
|
||||
|
||||
DevicePolicy = "auto"; # needed for hardware acceleration
|
||||
PrivateDevices = false; # needed for hardware acceleration
|
||||
AmbientCapabilities = [ "" ];
|
||||
CapabilityBoundingSet = [ "" ];
|
||||
ProtectSystem = "full";
|
||||
LockPersonality = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProcSubset = "pid";
|
||||
ProtectProc = "invisible";
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_UNIX"
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"~@cpu-emulation"
|
||||
"~@debug"
|
||||
"~@mount"
|
||||
"~@obsolete"
|
||||
"~@privileged"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
@ -982,6 +982,7 @@ in {
|
||||
stalwart-mail = handleTest ./stalwart-mail.nix {};
|
||||
stargazer = runTest ./web-servers/stargazer.nix;
|
||||
starship = handleTest ./starship.nix {};
|
||||
stash = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./stash.nix {};
|
||||
static-web-server = handleTest ./web-servers/static-web-server.nix {};
|
||||
step-ca = handleTestOn ["x86_64-linux"] ./step-ca.nix {};
|
||||
stratis = handleTest ./stratis {};
|
||||
|
80
nixos/tests/stash.nix
Normal file
80
nixos/tests/stash.nix
Normal file
@ -0,0 +1,80 @@
|
||||
import ./make-test-python.nix (
|
||||
let
|
||||
host = "127.0.0.1";
|
||||
port = 1234;
|
||||
dataDir = "/stash";
|
||||
in
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
name = "stash";
|
||||
meta.maintainers = pkgs.stash.meta.maintainers;
|
||||
|
||||
nodes.machine = {
|
||||
services.stash = {
|
||||
inherit dataDir;
|
||||
enable = true;
|
||||
|
||||
username = "test";
|
||||
passwordFile = pkgs.writeText "stash-password" "MyPassword";
|
||||
|
||||
jwtSecretKeyFile = pkgs.writeText "jwt_secret_key" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
sessionStoreKeyFile = pkgs.writeText "session_store_key" "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
||||
|
||||
plugins =
|
||||
let
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "stashapp";
|
||||
repo = "CommunityScripts";
|
||||
rev = "9b6fac4934c2fac2ef0859ea68ebee5111fc5be5";
|
||||
hash = "sha256-PO3J15vaA7SD4r/LyHlXjnpaeYAN9Q++O94bIWdz7OA=";
|
||||
};
|
||||
in
|
||||
[
|
||||
(pkgs.runCommand "stashNotes" { inherit src; } ''
|
||||
mkdir -p $out/plugins
|
||||
cp -r $src/plugins/stashNotes $out/plugins/stashNotes
|
||||
'')
|
||||
(pkgs.runCommand "Theme-Plex" { inherit src; } ''
|
||||
mkdir -p $out/plugins
|
||||
cp -r $src/themes/Theme-Plex $out/plugins/Theme-Plex
|
||||
'')
|
||||
];
|
||||
|
||||
mutableScrapers = true;
|
||||
scrapers =
|
||||
let
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "stashapp";
|
||||
repo = "CommunityScrapers";
|
||||
rev = "2ece82d17ddb0952c16842b0775274bcda598d81";
|
||||
hash = "sha256-AEmnvM8Nikhue9LNF9dkbleYgabCvjKHtzFpMse4otM=";
|
||||
};
|
||||
in
|
||||
[
|
||||
(pkgs.runCommand "FTV" { inherit src; } ''
|
||||
mkdir -p $out/scrapers/FTV
|
||||
cp -r $src/scrapers/FTV.yml $out/scrapers/FTV
|
||||
'')
|
||||
];
|
||||
|
||||
settings = {
|
||||
inherit host port;
|
||||
|
||||
stash = [ { path = "/srv"; } ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.wait_for_unit("stash.service")
|
||||
machine.wait_for_open_port(${toString port}, "${host}")
|
||||
machine.succeed("curl --fail http://${host}:${toString port}/")
|
||||
|
||||
with subtest("Test plugins/scrapers"):
|
||||
with subtest("mutable plugins directory should not exist"):
|
||||
machine.fail("test -d ${dataDir}/plugins")
|
||||
with subtest("mutable scrapers directory should exist and scraper FTV should be linked"):
|
||||
machine.succeed("test -L ${dataDir}/scrapers/FTV")
|
||||
'';
|
||||
}
|
||||
)
|
@ -1,50 +1,137 @@
|
||||
{
|
||||
buildGoModule,
|
||||
fetchFromGitHub,
|
||||
fetchYarnDeps,
|
||||
lib,
|
||||
nixosTests,
|
||||
nodejs,
|
||||
stash,
|
||||
stdenv,
|
||||
fetchurl,
|
||||
testers,
|
||||
yarnBuildHook,
|
||||
yarnConfigHook,
|
||||
}:
|
||||
let
|
||||
|
||||
version = "0.25.1";
|
||||
|
||||
sources = {
|
||||
x86_64-linux = {
|
||||
url = "https://github.com/stashapp/stash/releases/download/v${version}/stash-linux";
|
||||
hash = "sha256-Rb4x6iKx6T9NPuWWDbNaz+35XPzLqZzSm0psv+k2Gw4=";
|
||||
};
|
||||
aarch64-linux = {
|
||||
url = "https://github.com/stashapp/stash/releases/download/v${version}/stash-linux-arm64v8";
|
||||
hash = "sha256-6qPyIYKFkhmBNO47w9E91FSKlByepBOnl0MNJighGSc=";
|
||||
};
|
||||
x86_64-darwin = {
|
||||
url = "https://github.com/stashapp/stash/releases/download/v${version}/stash-macos";
|
||||
hash = "sha256-W8+rgqWUDTOB8ykGO2GL9tKEjaDXdx9LpFg0TAtJsxM=";
|
||||
};
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
inherit version;
|
||||
inherit (lib.importJSON ./version.json)
|
||||
gitHash
|
||||
srcHash
|
||||
vendorHash
|
||||
version
|
||||
yarnHash
|
||||
;
|
||||
|
||||
pname = "stash";
|
||||
|
||||
src = fetchurl { inherit (sources.${stdenv.system}) url hash; };
|
||||
src = fetchFromGitHub {
|
||||
owner = "stashapp";
|
||||
repo = "stash";
|
||||
tag = "v${version}";
|
||||
hash = srcHash;
|
||||
};
|
||||
|
||||
dontUnpack = true;
|
||||
frontend = stdenv.mkDerivation (final: {
|
||||
inherit version;
|
||||
pname = "${pname}-ui";
|
||||
src = "${src}/ui/v2.5";
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
yarnOfflineCache = fetchYarnDeps {
|
||||
yarnLock = "${final.src}/yarn.lock";
|
||||
hash = yarnHash;
|
||||
};
|
||||
|
||||
install -Dm755 $src $out/bin/stash
|
||||
nativeBuildInputs = [
|
||||
yarnConfigHook
|
||||
yarnBuildHook
|
||||
# Needed for executing package.json scripts
|
||||
nodejs
|
||||
];
|
||||
|
||||
runHook postInstall
|
||||
postPatch = ''
|
||||
substituteInPlace codegen.ts \
|
||||
--replace-fail "../../graphql/" "${src}/graphql/"
|
||||
'';
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
||||
export HOME=$(mktemp -d)
|
||||
export VITE_APP_DATE='1970-01-01 00:00:00'
|
||||
export VITE_APP_GITHASH=${gitHash}
|
||||
export VITE_APP_STASH_VERSION=v${version}
|
||||
export VITE_APP_NOLEGACY=true
|
||||
|
||||
yarn --offline run gqlgen
|
||||
yarn --offline build
|
||||
|
||||
mv build $out
|
||||
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
dontInstall = true;
|
||||
dontFixup = true;
|
||||
});
|
||||
in
|
||||
buildGoModule {
|
||||
inherit
|
||||
pname
|
||||
src
|
||||
version
|
||||
vendorHash
|
||||
;
|
||||
|
||||
ldflags = [
|
||||
"-s"
|
||||
"-w"
|
||||
"-X 'github.com/stashapp/stash/internal/build.buildstamp=1970-01-01 00:00:00'"
|
||||
"-X 'github.com/stashapp/stash/internal/build.githash=${gitHash}'"
|
||||
"-X 'github.com/stashapp/stash/internal/build.version=v${version}'"
|
||||
"-X 'github.com/stashapp/stash/internal/build.officialBuild=false'"
|
||||
];
|
||||
tags = [
|
||||
"sqlite_stat4"
|
||||
"sqlite_math_functions"
|
||||
];
|
||||
|
||||
subPackages = [ "cmd/stash" ];
|
||||
|
||||
preBuild = ''
|
||||
cp -a ${frontend} ui/v2.5/build
|
||||
# `go mod tidy` requires internet access and does nothing
|
||||
echo "skip_mod_tidy: true" >> gqlgen.yml
|
||||
# remove `-trimpath` fron `GOFLAGS` because `gqlgen` does not work with it
|
||||
GOFLAGS="''${GOFLAGS/-trimpath/}" go generate ./cmd/stash
|
||||
'';
|
||||
|
||||
meta = with lib; {
|
||||
description = "Stash is a self-hosted porn app";
|
||||
homepage = "https://github.com/stashapp/stash";
|
||||
license = licenses.agpl3Only;
|
||||
maintainers = with maintainers; [ Golo300 ];
|
||||
platforms = builtins.attrNames sources;
|
||||
mainProgram = "stash";
|
||||
strictDeps = true;
|
||||
|
||||
passthru = {
|
||||
inherit frontend;
|
||||
updateScript = ./update.py;
|
||||
tests = {
|
||||
inherit (nixosTests) stash;
|
||||
version = testers.testVersion {
|
||||
package = stash;
|
||||
version = "v${version} (${gitHash}) - Unofficial Build - 1970-01-01 00:00:00";
|
||||
};
|
||||
};
|
||||
};
|
||||
})
|
||||
|
||||
meta = {
|
||||
mainProgram = "stash";
|
||||
description = "Organizer for your adult videos/images";
|
||||
license = lib.licenses.agpl3Only;
|
||||
homepage = "https://stashapp.cc/";
|
||||
changelog = "https://github.com/stashapp/stash/blob/v${version}/ui/v2.5/src/docs/en/Changelog/v${lib.versions.major version}${lib.versions.minor version}0.md";
|
||||
maintainers = with lib.maintainers; [
|
||||
Golo300
|
||||
DrakeTDL
|
||||
];
|
||||
platforms = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
82
pkgs/by-name/st/stash/update.py
Executable file
82
pkgs/by-name/st/stash/update.py
Executable file
@ -0,0 +1,82 @@
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i python3 -p python3 prefetch-yarn-deps nix-prefetch-git nix-prefetch
|
||||
|
||||
from pathlib import Path
|
||||
from shutil import copyfile
|
||||
from urllib.request import Request, urlopen
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
def run_external(args: list[str]):
|
||||
proc = subprocess.run(
|
||||
args,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
|
||||
return proc.stdout.strip().decode("utf8")
|
||||
|
||||
def get_latest_release_tag():
|
||||
req = Request("https://api.github.com/repos/stashapp/stash/tags?per_page=1")
|
||||
|
||||
if "GITHUB_TOKEN" in os.environ:
|
||||
req.add_header("authorization", f"Bearer {os.environ['GITHUB_TOKEN']}")
|
||||
|
||||
with urlopen(req) as resp:
|
||||
return json.loads(resp.read())[0]
|
||||
|
||||
def prefetch_github(rev: str):
|
||||
print(f"Prefetching stashapp/stash({rev})")
|
||||
|
||||
proc = run_external(["nix-prefetch-git", "--no-deepClone", "--rev", rev, f"https://github.com/stashapp/stash"])
|
||||
|
||||
return json.loads(proc)
|
||||
|
||||
def prefetch_yarn(lock_file: str):
|
||||
print(f"Prefetching yarn deps")
|
||||
|
||||
hash = run_external(["prefetch-yarn-deps", lock_file])
|
||||
|
||||
return run_external(["nix", "hash", "convert", "--hash-algo", "sha256", hash])
|
||||
|
||||
def prefetch_go_modules(src: str, version: str):
|
||||
print(f"Prefetching go modules")
|
||||
expr = fr"""
|
||||
{{ sha256 }}: (buildGoModule {{
|
||||
pname = "stash";
|
||||
src = {src};
|
||||
version = "{version}";
|
||||
vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
|
||||
}}).goModules.overrideAttrs (_: {{ modSha256 = sha256; }})
|
||||
"""
|
||||
return run_external([
|
||||
"nix-prefetch",
|
||||
"--option",
|
||||
"extra-experimental-features",
|
||||
"flakes",
|
||||
expr
|
||||
])
|
||||
|
||||
|
||||
def save_version_json(version: dict[str, str]):
|
||||
print("Writing version.json")
|
||||
with open(Path(__file__).parent / "version.json", 'w') as f:
|
||||
json.dump(version, f, indent=2)
|
||||
f.write("\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
release = get_latest_release_tag()
|
||||
|
||||
src = prefetch_github(release['name'])
|
||||
|
||||
yarn_hash = prefetch_yarn(f"{src['path']}/ui/v2.5/yarn.lock")
|
||||
|
||||
save_version_json({
|
||||
"version": release["name"][1:],
|
||||
"gitHash": release["commit"]["sha"][:8],
|
||||
"srcHash": src["hash"],
|
||||
"yarnHash": yarn_hash,
|
||||
"vendorHash": prefetch_go_modules(src["path"], release["name"][1:])
|
||||
})
|
7
pkgs/by-name/st/stash/version.json
Normal file
7
pkgs/by-name/st/stash/version.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": "0.27.2",
|
||||
"gitHash": "76648fee",
|
||||
"srcHash": "sha256-SMZBDKqgVdXf2abaSf/FuG2Vodav7SBu6onjHFZIZIM=",
|
||||
"yarnHash": "sha256-ufGYQfEbcXO3XhpDQ3UTofS5B31L427KWy5NPbWhBJo=",
|
||||
"vendorHash": "sha256-ZtKKs0JCEe4OpPulO74qYTYrZu2Ds3prWp5N8UP6z0g="
|
||||
}
|
Loading…
Reference in New Issue
Block a user