Initial Commit
This commit is contained in:
5
src/routes/+layout.svelte
Normal file
5
src/routes/+layout.svelte
Normal file
@@ -0,0 +1,5 @@
|
||||
<script>
|
||||
import '../app.scss';
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
101
src/routes/+page.svelte
Normal file
101
src/routes/+page.svelte
Normal file
@@ -0,0 +1,101 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
|
||||
import Header from "./header.svelte";
|
||||
|
||||
import Highlight from 'svelte-highlight';
|
||||
import bash from 'svelte-highlight/languages/bash';
|
||||
import agate from 'svelte-highlight/styles/agate';
|
||||
|
||||
let Carousel;
|
||||
onMount(async () => {
|
||||
// Perform dynamic import of svelte-carousel because it breaks
|
||||
// if imported during SSR
|
||||
Carousel = (await import('svelte-carousel')).default;
|
||||
})
|
||||
|
||||
let images = [
|
||||
{
|
||||
path: "/lure-arch.gif",
|
||||
caption: "Recorded on Arch Linux with LURE commit 81013ce",
|
||||
alt: "GIF depicting an Arch Linux terminal with LURE installing a package"
|
||||
},
|
||||
{
|
||||
path: "/lure-debian.gif",
|
||||
caption: "Recorded on Debian Sid with LURE commit 81013ce",
|
||||
alt: "GIF depicting a Debian terminal with LURE installing a package"
|
||||
},
|
||||
{
|
||||
path: "/lure-fedora.gif",
|
||||
caption: "Recorded on Fedora 36 with LURE commit 81013ce",
|
||||
alt: "GIF depicting a Fedora terminal with LURE installing a package"
|
||||
},
|
||||
{
|
||||
path: "/lure-alpine.gif",
|
||||
caption: "Recorded on Alpine Linux 3.16 with commit 81013ce",
|
||||
alt: "GIF depicting an Alpine Linux terminal with LURE installing a package"
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
{@html agate}
|
||||
</svelte:head>
|
||||
|
||||
<Header/>
|
||||
|
||||
<section class="container">
|
||||
<p class="title">LURE</p>
|
||||
<p class="subtitle">The AUR missing from most Linux distributions</p>
|
||||
<hr>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<p class="card-header-title">Why should I use it?</p>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
LURE allows you to install your favorite software that may not be
|
||||
popular enough to be placed into your distro's repos, while preserving
|
||||
all the benefits of installing from repos, such as updates, easy
|
||||
uninstalling, etc. It also allows developers to provide a single place
|
||||
for their users to install their software.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<p class="card-header-title">How does it work?</p>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
LURE works by abstracting package formats and package managers. It
|
||||
builds native packages for your native package manager using AUR
|
||||
PKGBUILD-like bash scripts, then installs them using that package manager.
|
||||
This means that once LURE has successfully installed a package, it acts
|
||||
just like any other package and can be managed without LURE.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="title">Installation</p>
|
||||
<p>LURE can easily be installed by running its install script:</p>
|
||||
<Highlight language={bash} code='curl https://www.arsenm.dev/lure.sh | bash'/>
|
||||
<p>
|
||||
It's also available on the AUR as <a href="https://aur.archlinux.org/packages/lure-bin"><code>lure-bin</code></a> and distro
|
||||
packages are provided at the <a href="https://gitea.arsenm.dev/Arsen6331/lure/releases/latest">latest Gitea release</a>.
|
||||
</p>
|
||||
<br>
|
||||
<p class="title">Examples</p>
|
||||
|
||||
<!-- Use dynamically imported Carousel module -->
|
||||
<svelte:component this={Carousel}>
|
||||
{#each images as image}
|
||||
<figure class="has-text-centered">
|
||||
<img src={image.path} alt={image.alt ?? "GIF depicting LURE installing a package"}>
|
||||
<figcaption>{image.caption ?? ""}</figcaption>
|
||||
</figure>
|
||||
{/each}
|
||||
</svelte:component>
|
||||
|
||||
</section>
|
||||
52
src/routes/faq/+page.svelte
Normal file
52
src/routes/faq/+page.svelte
Normal file
@@ -0,0 +1,52 @@
|
||||
<script>
|
||||
import Header from "../header.svelte";
|
||||
</script>
|
||||
|
||||
<Header/>
|
||||
|
||||
<div class="container">
|
||||
<p class="title">FAQ</p>
|
||||
<br>
|
||||
<p class="subtitle" id='distro-support'>Why isn't my distro supported?</p>
|
||||
<p>
|
||||
In order to support a distro, LURE has to be able to talk to its package manager
|
||||
as well as build packages for it. Talking to the package manager is relatively simple,
|
||||
LURE just needs to be told which commands to run, but the package formats are much more
|
||||
complex. LURE uses <a href="https://github.com/goreleaser/nfpm">nFPM</a> to handle
|
||||
package formats, and nFPM supports only <code>deb</code>, <code>rpm</code>, <code>apk</code>,
|
||||
and <code>archlinux</code> package formats. When I started LURE, nFPM didn't have support for
|
||||
archlinux packages, so I added it in a PR. Despite Arch having a very simple package format, it took
|
||||
over 1k lines of code to implement, so supporting a distro with a different format, like
|
||||
<code>xbps</code> for Void Linux, is very complicated, and may take some time.
|
||||
</p>
|
||||
<hr>
|
||||
<p class="subtitle" id='flatpak-snap-appimage'>Why use LURE instead of Flatpak, Snap, or AppImage?</p>
|
||||
<p>
|
||||
LURE is not meant to solve the same problems as Flatpak, Snap, and AppImage. These are containerized
|
||||
package formats that allow the creation of a single package that works on all distros. This cross-platform package
|
||||
contains the program as well as everything else needed for that program to run. It works using
|
||||
something called a container, which can create issues where programs are slow to start, don't follow
|
||||
your system's settings, or can't access certain parts of your system. If you want most programs to work every
|
||||
time, or you're running an old distro with old packages, you should use containzerized formats.
|
||||
LURE, on the other hand, does not use containers. It builds the program from source automatically and
|
||||
installs it. It also doesn't have its own package format. Instead, it uses the same one as the distro
|
||||
it's running on, making LURE packages act the same way as your distro's packages. This means that
|
||||
unlike Snap and Flatpak, LURE is not a package manager. It simply uses your distro's package manager,
|
||||
which allows you to manage the packages installed by LURE even without it installed.
|
||||
</p>
|
||||
<br>
|
||||
<p>
|
||||
However, LURE also has some downsides. Because of the fact that it builds programs automatically from source,
|
||||
certain packages, especially git packages that retrieve the latest code from git, may not work on older distros,
|
||||
or distros like Debian which have old packages. Please consider your use case and whether such downsides are
|
||||
acceptable before using LURE. Also note that, like the AUR, all packages are user-submitted and are not vetted,
|
||||
so while unlikely, they may contain malicious code. It is the responsibility of the user to read the build script
|
||||
and ensure this is not the case. Malicious packages should be reported by opening an issue on the git repo containing them.
|
||||
</p>
|
||||
<hr>
|
||||
<p class="subtitle" id='adding-packages'>How do I add my own package to LURE?</p>
|
||||
<p>
|
||||
Take a look at the documentation for <a href="https://github.com/Arsen6331/lure/blob/master/docs/build-scripts.md">Build Scripts</a>
|
||||
and <a href="https://github.com/Arsen6331/lure/blob/master/docs/adding-packages.md">Adding Packages to LURE's repo</a>.
|
||||
</p>
|
||||
</div>
|
||||
30
src/routes/header.svelte
Normal file
30
src/routes/header.svelte
Normal file
@@ -0,0 +1,30 @@
|
||||
<script>
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import Icon from '@iconify/svelte';
|
||||
|
||||
function isActive(path) {
|
||||
return $page.route.id == path ? 'is-active' : ''
|
||||
}
|
||||
|
||||
let navbarIsActive = false;
|
||||
</script>
|
||||
|
||||
<nav class="navbar mb-5">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="/">LURE Web</a>
|
||||
<button class="navbar-burger {navbarIsActive ? 'is-active' : ''}" aria-label="menu" on:click={() => {navbarIsActive = !navbarIsActive}}>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="main-navbar" class="navbar-menu {navbarIsActive ? 'is-active' : ''}">
|
||||
<div class="navbar-end">
|
||||
<a class="navbar-item {isActive('/')}" href="/"><Icon icon="material-symbols:house" inline=true/> Home</a>
|
||||
<a class="navbar-item {isActive('/pkgs')}" href="/pkgs"><Icon icon="mdi:package-variant-closed" inline=true/> Packages</a>
|
||||
<a class="navbar-item {isActive('/faq')}" href="/faq"><Icon icon="mdi:question-mark-circle" inline=true/> FAQ</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
111
src/routes/pkg/[repo]/[name]/+page.svelte
Normal file
111
src/routes/pkg/[repo]/[name]/+page.svelte
Normal file
@@ -0,0 +1,111 @@
|
||||
<script>
|
||||
import { page } from '$app/stores';
|
||||
import { client } from 'twirpscript';
|
||||
import { GetPkg } from '$lib/lure.pb';
|
||||
import { LURE_WEB_API_URL } from '$env/static/public';
|
||||
import { DoubleBounce } from 'svelte-loading-spinners';
|
||||
|
||||
import Header from "../../../header.svelte";
|
||||
|
||||
import Icon from '@iconify/svelte';
|
||||
|
||||
client.baseURL = LURE_WEB_API_URL
|
||||
client.prefix = ""
|
||||
|
||||
function fullVer(pkg) {
|
||||
let ver = `${pkg.version}-${pkg.release}`;
|
||||
if (pkg.epoch != 0) {
|
||||
ver = `${pkg.epoch}:${ver}`
|
||||
}
|
||||
return ver
|
||||
}
|
||||
|
||||
function objToMap(o) {
|
||||
return new Map(Object.entries(o))
|
||||
}
|
||||
</script>
|
||||
|
||||
<Header/>
|
||||
|
||||
<section class="container">
|
||||
<a href="/pkgs"><Icon icon="material-symbols:arrow-back-rounded" inline=true/> Back</a>
|
||||
{#await GetPkg({name: $page.params.name, repository: $page.params.repo})}
|
||||
<center><DoubleBounce color="#375a7f" /></center>
|
||||
{:then pkg}
|
||||
<p class="title">{pkg.name}</p>
|
||||
<p class="subtitle mb-3">{fullVer(pkg)}</p>
|
||||
<a href="/pkg/{pkg.repository}/{pkg.name}/lure.sh" class="button is-primary mb-5">View lure.sh</a>
|
||||
<div class="box">
|
||||
<table class="table is-hoverable is-fullwidth">
|
||||
<tbody>
|
||||
{#if pkg.description != ""}
|
||||
<tr>
|
||||
<th>Description:</th>
|
||||
<td>{pkg.description}</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{#if pkg.homepage != ""}
|
||||
<tr>
|
||||
<th>Homepage:</th>
|
||||
<td><a href="{pkg.homepage}">{pkg.homepage}</a></td>
|
||||
</tr>
|
||||
{/if}
|
||||
{#if pkg.maintainer != ""}
|
||||
<tr>
|
||||
<th>Maintainer:</th>
|
||||
<td>{pkg.maintainer}</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{#if pkg.licenses.length != 0}
|
||||
<tr>
|
||||
<th>Licenses:</th>
|
||||
<td>
|
||||
{#each pkg.licenses as license, index}
|
||||
<a href="https://spdx.org/licenses/{license}.html">{license}</a>{#if index+1 < pkg.licenses.length}, {/if}
|
||||
{/each}
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{#if pkg.architectures.length != 0}
|
||||
<tr>
|
||||
<th>Architectures:</th>
|
||||
<td>{pkg.architectures.join(', ')}</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{#if pkg.conflicts.length != 0}
|
||||
<tr>
|
||||
<th>Conflicts:</th>
|
||||
<td>{pkg.conflicts.join(', ')}</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{#if pkg.provides.length != 0}
|
||||
<tr>
|
||||
<th>Provides:</th>
|
||||
<td>{pkg.provides.join(', ')}</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{#each [...objToMap(pkg.depends)] as [override, pkgList]}
|
||||
<tr>
|
||||
<th>Depends ({override == "" ? "default" : override}):</th>
|
||||
<td>{pkgList.entries.join(', ')}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
{#each [...objToMap(pkg.buildDepends)] as [override, pkgList]}
|
||||
<tr>
|
||||
<th>Build Depends ({override != "" ? override : "default"}):</th>
|
||||
<td>{pkgList.entries.join(', ')}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
<tr>
|
||||
<th>Repository:</th>
|
||||
<td>{pkg.repository}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{:catch err}
|
||||
<div class="notification is-danger my-3">
|
||||
Error: {err.msg}
|
||||
</div>
|
||||
{/await}
|
||||
</section>
|
||||
42
src/routes/pkg/[repo]/[name]/lure.sh/+page.svelte
Normal file
42
src/routes/pkg/[repo]/[name]/lure.sh/+page.svelte
Normal file
@@ -0,0 +1,42 @@
|
||||
<script>
|
||||
import { page } from '$app/stores';
|
||||
import { client } from 'twirpscript';
|
||||
import { GetBuildScript } from '$lib/lure.pb';
|
||||
import { LURE_WEB_API_URL } from '$env/static/public';
|
||||
import { DoubleBounce } from 'svelte-loading-spinners';
|
||||
|
||||
import Highlight from 'svelte-highlight';
|
||||
import bash from 'svelte-highlight/languages/bash';
|
||||
import agate from 'svelte-highlight/styles/agate';
|
||||
|
||||
import Header from "../../../../header.svelte";
|
||||
|
||||
import Icon from '@iconify/svelte';
|
||||
|
||||
client.baseURL = LURE_WEB_API_URL
|
||||
client.prefix = ""
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
{@html agate}
|
||||
</svelte:head>
|
||||
|
||||
<Header/>
|
||||
|
||||
<section class="container">
|
||||
<a href="."><Icon icon="material-symbols:arrow-back-rounded" inline=true/> Back</a>
|
||||
{#await GetBuildScript({name: $page.params.name, repository: $page.params.repo})}
|
||||
<center><DoubleBounce color="#375a7f" /></center>
|
||||
{:then resp}
|
||||
<p class="title">Build Script</p>
|
||||
<p class="subtitle">{$page.params.repo} / {$page.params.name}</p>
|
||||
<div class="box">
|
||||
<Highlight language={bash} code={resp.script}/>
|
||||
</div>
|
||||
{:catch err}
|
||||
<div class="notification is-danger my-3">
|
||||
Error: {err.msg}
|
||||
</div>
|
||||
{/await}
|
||||
</section>
|
||||
|
||||
121
src/routes/pkgs/+page.svelte
Normal file
121
src/routes/pkgs/+page.svelte
Normal file
@@ -0,0 +1,121 @@
|
||||
<script>
|
||||
import { client } from 'twirpscript';
|
||||
import { Search } from '$lib/lure.pb';
|
||||
import { searchReq } from '../../stores.js';
|
||||
import { LURE_WEB_API_URL } from '$env/static/public';
|
||||
|
||||
import Header from "../header.svelte";
|
||||
import { DoubleBounce } from 'svelte-loading-spinners';
|
||||
import Icon from '@iconify/svelte';
|
||||
|
||||
client.baseURL = LURE_WEB_API_URL
|
||||
client.prefix = ""
|
||||
|
||||
let searchPromise = Search(
|
||||
$searchReq ?? {
|
||||
query: "",
|
||||
limit: BigInt(0),
|
||||
sortBy: "UNSORTED",
|
||||
filterType: "NO_FILTER",
|
||||
}
|
||||
);
|
||||
|
||||
function onSubmit(e) {
|
||||
let formData = new FormData(e.target);
|
||||
let filterValue = formData.get('filter-value');
|
||||
let req = {
|
||||
query: formData.get('query'),
|
||||
limit: BigInt(0),
|
||||
sortBy: formData.get('sort-by'),
|
||||
filterType: formData.get('filter'),
|
||||
filterValue: filterValue == '' ? null : filterValue,
|
||||
}
|
||||
searchReq.set(req)
|
||||
searchPromise = Search(req);
|
||||
}
|
||||
|
||||
function onFilterChange(e) {
|
||||
let filterVal = document.getElementById('filter-val');
|
||||
|
||||
if (e.target.value != "NO_FILTER") {
|
||||
filterVal.classList.remove('is-hidden');
|
||||
} else {
|
||||
filterVal.classList.add('is-hidden');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Header/>
|
||||
|
||||
<section class="container">
|
||||
<p class="title">Package Search</p>
|
||||
<form on:submit|preventDefault={onSubmit}>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input class="input" type="text" name="query" value="" placeholder="Query">
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<div class="field">
|
||||
<div class="control select is-fullwidth">
|
||||
<select name="sort-by">
|
||||
<option value="UNSORTED" selected>Unsorted</option>
|
||||
<option value="NAME">Sort by Name</option>
|
||||
<option value="VERSION">Sort by Version</option>
|
||||
<option value="REPOSITORY">Sort by Repository</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="field">
|
||||
<div class="control select is-fullwidth">
|
||||
<select name="filter" on:change={onFilterChange}>
|
||||
<option value="NO_FILTER" selected>No Filter</option>
|
||||
<option value="IN_REPOSITORY">In Repo</option>
|
||||
<option value="SUPPORTS_ARCH">Supports Architecture</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input id="filter-val" class="input is-hidden" type="text" value="" name="filter-value" placeholder="Filter Value">
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" class="button is-fullwidth" value="Search">
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
{#await searchPromise}
|
||||
<center><DoubleBounce color="#375a7f" /></center>
|
||||
{:then resp}
|
||||
{#each resp.packages as pkg}
|
||||
<div class="card my-5">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">{pkg.repository} / {pkg.name}</p>
|
||||
<div class="card-header-icon">{pkg.version}</div>
|
||||
</header>
|
||||
|
||||
<div class="card-content">
|
||||
{pkg.description}
|
||||
</div>
|
||||
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item" href="/pkg/{pkg.repository}/{pkg.name}">More info <Icon icon="material-symbols:arrow-forward-rounded" inline=true/></a>
|
||||
</footer>
|
||||
</div>
|
||||
{/each}
|
||||
{#if resp.packages.length === 0}
|
||||
<p class="subtitle has-text-centered has-text-danger my-5 is-fullwidth">No Results</p>
|
||||
{/if}
|
||||
{:catch err}
|
||||
<div class="notification is-danger my-3">
|
||||
Error: {err.msg}
|
||||
</div>
|
||||
{/await}
|
||||
|
||||
</section>
|
||||
Reference in New Issue
Block a user