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