init
This commit is contained in:
commit
5bf7d0ef20
15 changed files with 452 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use nix
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.direnv
|
||||||
|
result
|
||||||
|
packages.json
|
||||||
|
options.json
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Dracula Theme
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# search.forkos.org
|
||||||
|
|
||||||
|
todo
|
13
default.nix
Normal file
13
default.nix
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
pins ? import ./npins,
|
||||||
|
pkgs ? import pins.nixpkgs { },
|
||||||
|
nixpkgsSource ? pins.nixpkgs,
|
||||||
|
}: let
|
||||||
|
lib = pkgs.lib;
|
||||||
|
in {
|
||||||
|
frontend = pkgs.callPackage ./frontend {};
|
||||||
|
updateIndex = pkgs.callPackage ./updateIndex { };
|
||||||
|
packagesJSON = pkgs.callPackage ./packagesJSON.nix { nixpkgs = nixpkgsSource; };
|
||||||
|
optionsJSON = pkgs.callPackage ./optionsJSON.nix { nixpkgs = nixpkgsSource; };
|
||||||
|
vmTest = "foo";
|
||||||
|
}
|
10
frontend/default.nix
Normal file
10
frontend/default.nix
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{ lib
|
||||||
|
, stdenv
|
||||||
|
}: stdenv.mkDerivation {
|
||||||
|
pname = "frontend";
|
||||||
|
version = "0.1.0";
|
||||||
|
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
# todo
|
||||||
|
}
|
106
frontend/index.html
Normal file
106
frontend/index.html
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/@meilisearch/instant-meilisearch/templates/basic_search.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="options">
|
||||||
|
<select onchange="kind()" size=2 id="kind">
|
||||||
|
<option value="packages">packages</option>
|
||||||
|
<option value="modules">modules</option>
|
||||||
|
</select>
|
||||||
|
<select onchange="release()" size=2 id="release">
|
||||||
|
<option value="rolling">rolling</option>
|
||||||
|
<option value="stable">stable</option>
|
||||||
|
</select>
|
||||||
|
<div id="sort-by"></div>
|
||||||
|
<div id="packages_filter" class="filter">
|
||||||
|
<h2>Platform</h2>
|
||||||
|
<div id="platform"></div>
|
||||||
|
</div>
|
||||||
|
<div id="modules_filter" class="filter">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wrapper">
|
||||||
|
<div id="searchbox" focus></div>
|
||||||
|
<div id="hits"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script
|
||||||
|
src="https://cdn.jsdelivr.net/npm/@meilisearch/instant-meilisearch/dist/instant-meilisearch.umd.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4"></script>
|
||||||
|
<script>
|
||||||
|
function kind() {
|
||||||
|
k = document.getElementById("kind").value;
|
||||||
|
alert(k);
|
||||||
|
}
|
||||||
|
function release() {
|
||||||
|
r = document.getElementById("release").value;
|
||||||
|
search.helper.setIndex("packages" + "-" + r).search()
|
||||||
|
}
|
||||||
|
const search = instantsearch({
|
||||||
|
indexName: "packages-rolling",
|
||||||
|
// implement params for stable/rolling
|
||||||
|
// https://marcus-obst.de/blog/algolia-instantsearch-router-handle-existing-url-parameters
|
||||||
|
routing: true,
|
||||||
|
searchClient: instantMeiliSearch(
|
||||||
|
"https://meilisearch.aq0.de/"
|
||||||
|
).searchClient
|
||||||
|
});
|
||||||
|
search.addWidgets([
|
||||||
|
// // todo: figure out sortableIndex first
|
||||||
|
// // https://www.meilisearch.com/docs/learn/filtering_and_sorting/sort_search_results#configure-meilisearch-for-sorting-at-search-time
|
||||||
|
// // https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
|
||||||
|
// instantsearch.widgets.sortBy({
|
||||||
|
// container: '#sort-by',
|
||||||
|
// items: [
|
||||||
|
// {value: 'steam-video-games', label: 'Relevant'},
|
||||||
|
// {
|
||||||
|
// value: 'steam-video-games:recommendationCount:desc',
|
||||||
|
// label: 'Most Recommended',
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// value: 'steam-video-games:recommendationCount:asc',
|
||||||
|
// label: 'Least Recommended',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// }),
|
||||||
|
instantsearch.widgets.refinementList({
|
||||||
|
container: "#platform",
|
||||||
|
attribute: "meta.platforms",
|
||||||
|
}),
|
||||||
|
instantsearch.widgets.sortBy({
|
||||||
|
container: '#sort-by',
|
||||||
|
items: [
|
||||||
|
{value: 'steam-video-games', label: 'Relevant'},
|
||||||
|
{
|
||||||
|
value: 'steam-video-games:recommendationCount:desc',
|
||||||
|
label: 'Most Recommended',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'steam-video-games:recommendationCount:asc',
|
||||||
|
label: 'Least Recommended',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
instantsearch.widgets.searchBox({container: "#searchbox"}),
|
||||||
|
instantsearch.widgets.configure({hitsPerPage: 8}),
|
||||||
|
instantsearch.widgets.hits({
|
||||||
|
container: "#hits",
|
||||||
|
templates: {
|
||||||
|
item: `
|
||||||
|
<div>
|
||||||
|
<div class="hit-name">
|
||||||
|
{{#helpers.highlight}}{"attribute": "name"}{{/helpers.highlight}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
})]);
|
||||||
|
search.start();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
85
frontend/options.html
Normal file
85
frontend/options.html
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/@meilisearch/instant-meilisearch/templates/basic_search.css" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="options">
|
||||||
|
<select onchange="kind()" size=2 id="kind">
|
||||||
|
<option value="packages">packages</option>
|
||||||
|
<option value="modules">modules</option>
|
||||||
|
</select>
|
||||||
|
<select onchange="release()" size=2 id="release">
|
||||||
|
<option value="rolling">rolling</option>
|
||||||
|
<option value="stable">stable</option>
|
||||||
|
</select>
|
||||||
|
<div id="sort-by"></div>
|
||||||
|
<!-- <div id="packages_filter" class="filter"> -->
|
||||||
|
<!-- <h2>Platform</h2> -->
|
||||||
|
<!-- <div id="platform"></div> -->
|
||||||
|
<!-- </div> -->
|
||||||
|
<div id="modules_filter" class="filter">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wrapper">
|
||||||
|
<div id="searchbox" focus></div>
|
||||||
|
<div id="hits"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script
|
||||||
|
src="https://cdn.jsdelivr.net/npm/@meilisearch/instant-meilisearch/dist/instant-meilisearch.umd.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/instantsearch.js@4"></script>
|
||||||
|
<script>
|
||||||
|
function kind() {
|
||||||
|
k = document.getElementById("kind").value;
|
||||||
|
alert(k);
|
||||||
|
}
|
||||||
|
function release() {
|
||||||
|
r = document.getElementById("release").value;
|
||||||
|
search.helper.setIndex("options" + "-" + r).search()
|
||||||
|
}
|
||||||
|
const search = instantsearch({
|
||||||
|
indexName: "options-rolling",
|
||||||
|
// implement params for stable/rolling
|
||||||
|
// https://marcus-obst.de/blog/algolia-instantsearch-router-handle-existing-url-parameters
|
||||||
|
routing: true,
|
||||||
|
searchClient: instantMeiliSearch(
|
||||||
|
"https://meilisearch.aq0.de/"
|
||||||
|
).searchClient
|
||||||
|
});
|
||||||
|
search.addWidgets([
|
||||||
|
instantsearch.widgets.sortBy({
|
||||||
|
container: '#sort-by',
|
||||||
|
items: [
|
||||||
|
{value: 'steam-video-games', label: 'Relevant'},
|
||||||
|
{
|
||||||
|
value: 'steam-video-games:recommendationCount:desc',
|
||||||
|
label: 'Most Recommended',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'steam-video-games:recommendationCount:asc',
|
||||||
|
label: 'Least Recommended',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
instantsearch.widgets.searchBox({container: "#searchbox"}),
|
||||||
|
instantsearch.widgets.configure({hitsPerPage: 8}),
|
||||||
|
instantsearch.widgets.hits({
|
||||||
|
container: "#hits",
|
||||||
|
templates: {
|
||||||
|
item: `
|
||||||
|
<div>
|
||||||
|
<div class="hit-name">
|
||||||
|
{{#helpers.highlight}}{"attribute": "name"}{{/helpers.highlight}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
})]);
|
||||||
|
search.start();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
80
npins/default.nix
Normal file
80
npins/default.nix
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
# Generated by npins. Do not modify; will be overwritten regularly
|
||||||
|
let
|
||||||
|
data = builtins.fromJSON (builtins.readFile ./sources.json);
|
||||||
|
version = data.version;
|
||||||
|
|
||||||
|
mkSource =
|
||||||
|
spec:
|
||||||
|
assert spec ? type;
|
||||||
|
let
|
||||||
|
path =
|
||||||
|
if spec.type == "Git" then
|
||||||
|
mkGitSource spec
|
||||||
|
else if spec.type == "GitRelease" then
|
||||||
|
mkGitSource spec
|
||||||
|
else if spec.type == "PyPi" then
|
||||||
|
mkPyPiSource spec
|
||||||
|
else if spec.type == "Channel" then
|
||||||
|
mkChannelSource spec
|
||||||
|
else
|
||||||
|
builtins.throw "Unknown source type ${spec.type}";
|
||||||
|
in
|
||||||
|
spec // { outPath = path; };
|
||||||
|
|
||||||
|
mkGitSource =
|
||||||
|
{
|
||||||
|
repository,
|
||||||
|
revision,
|
||||||
|
url ? null,
|
||||||
|
hash,
|
||||||
|
branch ? null,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
assert repository ? type;
|
||||||
|
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
|
||||||
|
# In the latter case, there we will always be an url to the tarball
|
||||||
|
if url != null then
|
||||||
|
(builtins.fetchTarball {
|
||||||
|
inherit url;
|
||||||
|
sha256 = hash; # FIXME: check nix version & use SRI hashes
|
||||||
|
})
|
||||||
|
else
|
||||||
|
assert repository.type == "Git";
|
||||||
|
let
|
||||||
|
urlToName =
|
||||||
|
url: rev:
|
||||||
|
let
|
||||||
|
matched = builtins.match "^.*/([^/]*)(\\.git)?$" repository.url;
|
||||||
|
|
||||||
|
short = builtins.substring 0 7 rev;
|
||||||
|
|
||||||
|
appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else "";
|
||||||
|
in
|
||||||
|
"${if matched == null then "source" else builtins.head matched}${appendShort}";
|
||||||
|
name = urlToName repository.url revision;
|
||||||
|
in
|
||||||
|
builtins.fetchGit {
|
||||||
|
url = repository.url;
|
||||||
|
rev = revision;
|
||||||
|
inherit name;
|
||||||
|
# hash = hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkPyPiSource =
|
||||||
|
{ url, hash, ... }:
|
||||||
|
builtins.fetchurl {
|
||||||
|
inherit url;
|
||||||
|
sha256 = hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkChannelSource =
|
||||||
|
{ url, hash, ... }:
|
||||||
|
builtins.fetchTarball {
|
||||||
|
inherit url;
|
||||||
|
sha256 = hash;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
if version == 3 then
|
||||||
|
builtins.mapAttrs (_: mkSource) data.pins
|
||||||
|
else
|
||||||
|
throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"
|
11
npins/sources.json
Normal file
11
npins/sources.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"pins": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"type": "Channel",
|
||||||
|
"name": "nixpkgs-unstable",
|
||||||
|
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-24.11pre656183.453402b94f39/nixexprs.tar.xz",
|
||||||
|
"hash": "16lpbplz7f5ximc7j10qimr043xa89lx470ycb150529828y21wm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": 3
|
||||||
|
}
|
25
optionsJSON.nix
Normal file
25
optionsJSON.nix
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{ lib, stdenv, jq, nixosOptionsDoc, nixpkgs }: let
|
||||||
|
# NB: This file describes the Nixpkgs manual, which happens to use module
|
||||||
|
# docs infra originally developed for NixOS.
|
||||||
|
optionsDoc = nixosOptionsDoc {
|
||||||
|
inherit (lib.evalModules {
|
||||||
|
modules = import "${nixpkgs}/nixos/modules/module-list.nix" ++ [ { nixpkgs.hostPlatform = "x86_64-linux"; } ];
|
||||||
|
class = "nixpkgsConfig";
|
||||||
|
}) options;
|
||||||
|
# documentType = "none";
|
||||||
|
# transformOptions = opt: opt;
|
||||||
|
};
|
||||||
|
in stdenv.mkDerivation {
|
||||||
|
name = "options.json";
|
||||||
|
src = nixpkgs;
|
||||||
|
|
||||||
|
buildInputs = [ jq ];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
jq -e 'to_entries | map(.key as $k | .value + {"id":($k | gsub("[^a-zA-Z0-9-_]"; "-")), "name":$k})' ${optionsDoc.optionsJSON}/share/doc/nixos/options.json > $out
|
||||||
|
'';
|
||||||
|
|
||||||
|
dontUnpack = true;
|
||||||
|
dontBuild = false;
|
||||||
|
doCheck = false;
|
||||||
|
}
|
18
packagesJSON.nix
Normal file
18
packagesJSON.nix
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{ stdenv
|
||||||
|
, nixpkgs
|
||||||
|
, jq
|
||||||
|
, lix
|
||||||
|
}: stdenv.mkDerivation {
|
||||||
|
name = "packages.json";
|
||||||
|
src = nixpkgs;
|
||||||
|
|
||||||
|
buildInputs = [ jq lix ];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
NIX_STATE_DIR=$TMPDIR NIX_PATH= nix-env -f $src -qa --meta --json --show-trace --arg config "import $src/pkgs/top-level/packages-config.nix" | jq -e 'to_entries | map(.key as $k | .value + {"id":($k | gsub("[^a-zA-Z0-9-_]"; "-")), "name":$k})' > $out
|
||||||
|
'';
|
||||||
|
|
||||||
|
dontUnpack = true;
|
||||||
|
dontBuild = false;
|
||||||
|
doCheck = false;
|
||||||
|
}
|
10
shell.nix
Normal file
10
shell.nix
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
let
|
||||||
|
sources = import ./npins;
|
||||||
|
pkgs = import sources.nixpkgs { };
|
||||||
|
in pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
npins
|
||||||
|
python3
|
||||||
|
python3Packages.meilisearch
|
||||||
|
];
|
||||||
|
}
|
8
updateIndex/default.nix
Normal file
8
updateIndex/default.nix
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{ python3Packages
|
||||||
|
, writers
|
||||||
|
}: writers.writePython3Bin "updateIndex" {
|
||||||
|
flakeIgnore = [ "E501" ];
|
||||||
|
libraries = with python3Packages; [
|
||||||
|
meilisearch
|
||||||
|
];
|
||||||
|
} (builtins.readFile ./default.py)
|
57
updateIndex/default.py
Normal file
57
updateIndex/default.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import meilisearch
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def meiliTaskStatus(task_uid: int, verbose: bool):
|
||||||
|
taskInfo = client.get_task(task_uid)
|
||||||
|
while taskInfo.status != "succeeded" and taskInfo.status != "canceled" and taskInfo.status != "failed":
|
||||||
|
# no need to hammer the api
|
||||||
|
time.sleep(3)
|
||||||
|
taskInfo = client.get_task(task_uid)
|
||||||
|
if verbose:
|
||||||
|
print(taskInfo)
|
||||||
|
else:
|
||||||
|
if taskInfo.status == "succeeded":
|
||||||
|
print(f"job {taskInfo.type} for {taskInfo.index_uid} ({taskInfo.uid}) succeeded")
|
||||||
|
elif taskInfo.status == "canceled":
|
||||||
|
print(f"job {taskInfo.type} for {taskInfo.index_uid} ({taskInfo.uid}) was canceled")
|
||||||
|
elif taskInfo.status == "failed":
|
||||||
|
print(f"job {taskInfo.type} for {taskInfo.index_uid} ({taskInfo.uid}) failed")
|
||||||
|
else:
|
||||||
|
raise ValueError(f"unexpected job task type for: {taskInfo}")
|
||||||
|
|
||||||
|
|
||||||
|
# todo: error and figure out a sensible default via envrc and maybe a local instance with something like devenv
|
||||||
|
meiliInstance = os.getenv("MEILI_INSTANCE")
|
||||||
|
if meiliInstance is None:
|
||||||
|
meiliInstance = "https://meilisearch.aq0.de/"
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog='updateIndex',
|
||||||
|
description='Updates meilisearch indexs, inteded to be be used for search.forkos.org'
|
||||||
|
)
|
||||||
|
parser.add_argument('documentsJsonFile')
|
||||||
|
parser.add_argument('-k', '--kind', choices=['packages', 'options'], default='packages')
|
||||||
|
parser.add_argument('-r', '--release', choices=['rolling', 'stable'], default='rolling')
|
||||||
|
parser.add_argument('-v', '--verbose', action='store_true')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
client = meilisearch.Client(meiliInstance)
|
||||||
|
indexName = f"{args.kind}-{args.release}"
|
||||||
|
|
||||||
|
with open(args.documentsJsonFile) as json_file:
|
||||||
|
documents = json.load(json_file)
|
||||||
|
|
||||||
|
if args.kind == "packages":
|
||||||
|
documentResponse = client.index(indexName).add_documents(documents)
|
||||||
|
indexResponse = client.index(indexName).update_filterable_attributes(['meta'])
|
||||||
|
meiliTaskStatus(documentResponse.task_uid, args.verbose)
|
||||||
|
meiliTaskStatus(indexResponse.task_uid, args.verbose)
|
||||||
|
elif args.kind == "options":
|
||||||
|
documentResponse = client.index(indexName).add_documents(documents)
|
||||||
|
meiliTaskStatus(documentResponse.task_uid, args.verbose)
|
||||||
|
else:
|
||||||
|
raise ValueError("invalid value for type kind")
|
Loading…
Reference in a new issue