Initial attempt at CI/CD
Dependencies
- [2]
D4SBRY6KInitial patch - [3]
X7GJK2QDUpdate - [4]
STNLKCHXnpm -> pnpm migration - [5]
LE34RHBBFixing a CORS bug - [6]
IFYNLE5HCargo.nix - [7]
5VX32NQSDebugging settings/repos - [8]
JSRKEVVPDependencies upgrade - [9]
Y4NVVY56Formatting - [10]
O4MSEBYCHeader/Footer format - [11]
HSHYU5QMChangelist: proper navigation + identicons
Change contents
- edit in ui/vite.config.js at line 7
css: {preprocessorOptions: {scss: {additionalData(source, fp) {if (fp.endsWith('app.scss')) return source;return '@import "/src/app.scss";' + source;}}}}, - edit in ui/vite.config.js at line 35
'/recover': {target: 'http://127.0.0.1:8001',secure: false}, - edit in ui/svelte.config.js at line 3
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; - replacement in ui/svelte.config.js at line 7
preprocess: [mdsvex()],compilerOptions: {runes: true},preprocess: [vitePreprocess({script: true,style: true}),mdsvex()], - file addition: static[2.14]
- file addition: hljs.css[0.544]
.hljs-emphasis {font-style: italic;}.hljs-strong {font-weight: 700;}@media (prefers-color-scheme: dark) {.hljs {color: #fff;background: #1c1b1b;}.hljs-subst {color: #fff;}.hljs-comment {color: #999;}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag {color: #88aece;}.hljs-attribute {color: #c59bc1;}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type {color: #f08d49;}.hljs-selector-class {color: #88aece;}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable {color: #b5bd68;}.hljs-meta,.hljs-selector-pseudo {color: #88aece;}.hljs-built_in,.hljs-literal,.hljs-title {color: #f08d49;}.hljs-bullet,.hljs-code {color: #ccc;}.hljs-meta .hljs-string {color: #b5bd68;}.hljs-deletion {color: #de7176;}.hljs-addition {color: #76c490;}}@media (prefers-color-scheme: light) {.hljs {color: #2f3337;background: #f6f6f6;}.hljs-subst {color: #2f3337;}.hljs-comment {color: #656e77;}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag {color: #015692;}.hljs-attribute {color: #803378;}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type {color: #b75501;}.hljs-selector-class {color: #015692;}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable {color: #54790d;}.hljs-meta,.hljs-selector-pseudo {color: #015692;}.hljs-built_in,.hljs-literal,.hljs-title {color: #b75501;}.hljs-bullet,.hljs-code {color: #535a60;}.hljs-meta .hljs-string {color: #54790d;}.hljs-deletion {color: #c02d2e;}.hljs-addition {color: #2f6f44;}} - file deletion: app.css
@import 'tailwindcss';@plugin "daisyui";@plugin "@iconify/tailwind4";@layer base {}h1 {font-size: var(--text-2xl) !important;}h2 {font-size: var(--text-xl) !important;}h3 {font-size: var(--text-l) !important;} - edit in ui/src/routes/terms/abuse/+page.svx at line 43
h3 { font-size: 15pt; } - edit in ui/src/routes/terms/+page.svx at line 106
h3 { font-size: 15pt; } - edit in ui/src/routes/settings/ssh/+page.svelte at line 4
export let data: PageData; - replacement in ui/src/routes/settings/ssh/+page.svelte at line 5
console.log('data', data);const { data } = $props(); - replacement in ui/src/routes/settings/ssh/+page.svelte at line 7
let m: any = null;let m: any = $state(null); - replacement in ui/src/routes/settings/account/+page.svelte at line 3
export let data: PageData;const { data } = $props(); - edit in ui/src/routes/settings/+page.svelte at line 5
export let data: PageData; - replacement in ui/src/routes/settings/+page.svelte at line 6
console.log('data', data);const { data } = $props(); - replacement in ui/src/routes/recover/+page.svelte at line 2
export let data: null | { code: string };const { data }: { data: null | { code: string } } = $props(); - edit in ui/src/routes/helpers.ts at line 3
import { browser } from '$app/environment'; - replacement in ui/src/routes/helpers.ts at line 12
export const server = import.meta.env.VITE_SERVERexport const server = import.meta.env.VITE_SERVER; - replacement in ui/src/routes/[user]/[repo]/tree/[[pos]]/+page.svelte at line 8
export let data: PageData;const channel = data.channel.length ? data.channel : 'main';const { data } = $props();const channel = $derived(data.channel.length ? data.channel : 'main'); - edit in ui/src/routes/[user]/[repo]/tree/[[pos]]/+page.svelte at line 14
<link rel="stylesheet" href="/hljs.css" /> - edit in ui/src/routes/[user]/[repo]/tree/[[pos]]/+page.svelte at line 82
:global {.hljs-emphasis {font-style: italic;}.hljs-strong {font-weight: 700;}@media (prefers-color-scheme: dark) {.hljs {color: #fff;background: #1c1b1b;}.hljs-subst {color: #fff;}.hljs-comment {color: #999;}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag {color: #88aece;}.hljs-attribute {color: #c59bc1;}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type {color: #f08d49;}.hljs-selector-class {color: #88aece;}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable {color: #b5bd68;}.hljs-meta,.hljs-selector-pseudo {color: #88aece;}.hljs-built_in,.hljs-literal,.hljs-title {color: #f08d49;}.hljs-bullet,.hljs-code {color: #ccc;}.hljs-meta .hljs-string {color: #b5bd68;}.hljs-deletion {color: #de7176;}.hljs-addition {color: #76c490;}}@media (prefers-color-scheme: light) {.hljs {color: #2f3337;background: #f6f6f6;}.hljs-subst {color: #2f3337;}.hljs-comment {color: #656e77;}.hljs-attr,.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-section,.hljs-selector-tag {color: #015692;}.hljs-attribute {color: #803378;}.hljs-name,.hljs-number,.hljs-quote,.hljs-selector-id,.hljs-template-tag,.hljs-type {color: #b75501;}.hljs-selector-class {color: #015692;}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-string,.hljs-symbol,.hljs-template-variable,.hljs-variable {color: #54790d;}.hljs-meta,.hljs-selector-pseudo {color: #015692;}.hljs-built_in,.hljs-literal,.hljs-title {color: #b75501;}.hljs-bullet,.hljs-code {color: #535a60;}.hljs-meta .hljs-string {color: #54790d;}.hljs-deletion {color: #c02d2e;}.hljs-addition {color: #2f6f44;}}} - file addition: jobs[2.54792]
- file addition: [job][0.3422]
- file addition: +page.ts[0.3441]
import type { PageLoad } from './$types';import { errorMsg, server } from '../../../../helpers';export const load: PageLoad = async ({ fetch, params }) => {const u = `${server}/api/job/${params.user}/${params.repo}/${params.job}`;const resp = await fetch(u, { credentials: 'include' });if (resp.status == 200) {return await resp.json();} else {const y = await resp.json();errorMsg(resp.status, y);}}; - file addition: +page.svelte[0.3441]
<script lang="ts">import Nav from '../../Nav.svelte';import Tabs from '../../Tabs.svelte';import { onMount } from 'svelte';import { server } from '../../../../helpers';const { data, params } = $props();let ended = $derived(data.ended);let status = $derived(data.status);import { AnsiUp } from 'ansi_up';let socket: WebSocket;let out: string[][] = $state([[], []]);onMount(() => {let ansi_up = new AnsiUp();socket = new WebSocket(`${server}/api/job/${params.user}/${params.repo}/${params.job}/ws`);socket.onmessage = (event) => {console.log(event);const m = JSON.parse(event.data);if ('Chunk' in m) {console.log(m);out[m.Chunk.channel].push(ansi_up.ansi_to_html(m.Chunk.content));} else if ('Status' in m) {console.log(m);ended = m.Status.ended;status = m.Status.status;}};socket.onopen = () => {// socket.send('{ "State": { "stdout": 0, "stderr": 0 } }');};});</script><svelte:head><title>{params.user} / {params.repo}</title></svelte:head><div class="p-3"><Nav user={params.user} repo={params.repo} path={[]} link={true} /><Tabs user={params.user} repo={params.repo} channel={data.channel} active="jobs" /><h1 class="pt-10">Job {data.id}</h1><div class="py-10">Started at {data.started}{#if ended}, ended at {ended} with status {status}{/if}.</div><h2 class="mt-5">Stdout</h2><div class="p-5"><div class="w-full whitespace-pre-wrap font-mono text-sm">{#each out[0] as m}{@html m}{/each}</div>{#if !out[0].length}<p>No stdout so far</p>{/if}</div><h2 class="mt-10">Stderr</h2><div class="p-5"><div class="w-full whitespace-pre-wrap font-mono text-sm">{#each out[1] as m}{@html m}{/each}</div>{#if !out[1].length}<p>No stderr so far</p>{/if}</div></div> - file addition: +page.ts[0.3422]
import type { PageLoad } from './$types';import { errorMsg, server } from '../../../helpers';export const load: PageLoad = async ({ fetch, params }) => {const u = `${server}/api/job/${params.user}/${params.repo}`;const resp = await fetch(u, { credentials: 'include' });if (resp.status == 200) {return await resp.json();} else {const y = await resp.json();errorMsg(resp.status, y);}}; - file addition: +page.svelte[0.3422]
<script lang="ts">import Nav from '../Nav.svelte';import Tabs from '../Tabs.svelte';import { resolve } from '$app/paths';const { data, params } = $props();</script><svelte:head><title>{data.owner} / {data.repo}</title></svelte:head><div class="p-3"><Nav user={params.user} repo={params.repo} path={[]} link={true} /><Tabsuser={params.user}repo={params.repo}login={data.user}channel={data.channel}active="jobs" /><div class="p-3"><h1 class="mt-5">Jobs</h1><table class="mt-10 table"><thead> <tr><td>Job</td><td>Started</td><td>Ended</td><td>Status</td> </tr></thead><tbody>{#each data.jobs as job (job.id)}<tr><td><aclass="link link-primary"href={resolve('/[user]/[repo]/jobs/[job]', {job: job.id,...params})}>{job.id}</a></td><td>{job.started}</td><td>{job.ended}</td><td>{job.status}</td></tr>{/each}</tbody></table></div></div> - replacement in ui/src/routes/[user]/[repo]/discussion/new/+page.svelte at line 6
export let data: {owner: string;repo: string;login: string;unique: number;token: string;};console.log('data', data);const {data}: {data: {owner: string;repo: string;login: string;unique: number;token: string;};} = $props(); - replacement in ui/src/routes/[user]/[repo]/discussion/[disc]/+page.svelte at line 57
export let data: {owner: string;repo: string;login: string;email: string;edit_comment?: string;d: DiscussionT;channels: string[];default_channel: string;comments: DiscussionItem[];uniq: number;token: string;};const {data}: {data: {owner: string;repo: string;login: string;email: string;edit_comment?: string;d: DiscussionT;channels: string[];default_channel: string;comments: DiscussionItem[];uniq: number;token: string;};} = $props(); - replacement in ui/src/routes/[user]/[repo]/discussion/Title.svelte at line 2
export let n: number | null = null;export let edit = false;export let title = '';const {n,edit = false,title = ''}: {n?: number;edit: boolean;title: string;} = $props(); - replacement in ui/src/routes/[user]/[repo]/discussion/Tag.svelte at line 16
export let tag: Tag;const { tag }: { tag: Tag } = $props(); - replacement in ui/src/routes/[user]/[repo]/discussion/Patch.svelte at line 3
export let hash: string;export let id: string;export let disc: number;export let timestamp: number;export let pushed_by: string;export let removed: number | undefined;export let can_add: boolean = false;export let can_remove: boolean = false;export let owner: string;export let repo: string;export let token: string;export let authors: {login?: string;name?: string;key: string;}[] = [];export let header:| {message: string;timestamp: string;}| undefined;console.log('HEADER', JSON.stringify(header));const {hash,id,disc,timestamp,pushed_by,removed,can_add = false,can_remove = false,owner,repo,token,authors,header}: {hash: string;id: string;disc: number;timestamp: number;pushed_by: string;removed?: number;can_add: boolean;can_remove: boolean;owner: string;repo: string;token: string;authors: {login?: string;name?: string;key: string;}[];header?: {message: string;timestamp: string;};} = $props(); - edit in ui/src/routes/[user]/[repo]/discussion/Comment.svelte at line 2
import { assets } from '$app/paths'; - edit in ui/src/routes/[user]/[repo]/discussion/Comment.svelte at line 4
// export let author = '';// export let id = '';export let disc: { closed?: number; n: number } | undefined = undefined;// export let timestamp: number;// export let value = '';// export let value_html = '';export let owner: string;export let repo: string;export let edit: boolean = false;export let login: string;export let token: string;export let uniq: number; - replacement in ui/src/routes/[user]/[repo]/discussion/Comment.svelte at line 5
export let comment: Comment = {author: login,id: '',timestamp: 0,content: '',content_html: ''};let {disc,owner,repo,edit = false,login,token,uniq,comment = {author: login,id: '',timestamp: 0,content: '',content_html: ''}}: {disc?: { closed?: number; n: number };owner: string;repo: string;edit: boolean;login: string;token: string;uniq: string;comment: Comment;} = $props(); - replacement in ui/src/routes/[user]/[repo]/discussion/Comment.svelte at line 55
on:click={() => (edit = true)}>Edit</button>onclick={() => (edit = true)}>Edit</button> - edit in ui/src/routes/[user]/[repo]/discussion/+page.svelte at line 5
import type { PageData } from './$types'; - replacement in ui/src/routes/[user]/[repo]/discussion/+page.svelte at line 6
export let data: PageData;let includeClosed = false;const { data } = $props();let includeClosed = $state(false); - replacement in ui/src/routes/[user]/[repo]/discussion/+page.svelte at line 25
on:change={() => {onchange={() => { - replacement in ui/src/routes/[user]/[repo]/change/[hash]/Pos.svelte at line 3
export let c: Position | Vertex;export let deps: Deps;const { c, deps }: { c: Position | Vertex; deps: Deps } = $props(); - replacement in ui/src/routes/[user]/[repo]/change/[hash]/Pos.svelte at line 5
let d: string | null = null;if (deps && deps.hashes[c.change]) {d = deps.hashes[c.change];}const d: string | null = $derived.by(() => {if (deps && deps.hashes[c.change]) {return deps.hashes[c.change];} else {return null;}}); - replacement in ui/src/routes/[user]/[repo]/change/[hash]/Meta.svelte at line 3
export let m: Meta;let dir = (m.metadata & 0x200) != 0 ? 'd' : '-';let p =const { m }: { m: Meta } = $props();const dir = $derived((m.metadata & 0x200) != 0 ? 'd' : '-');const p = $derived( - replacement in ui/src/routes/[user]/[repo]/change/[hash]/Meta.svelte at line 7
(m.metadata & 0x80 ? 'w' : '-') +(m.metadata & 0x40 ? 'r' : '-') +(m.metadata & 0x20 ? 'x' : '-') +(m.metadata & 0x10 ? 'w' : '-') +(m.metadata & 0x8 ? 'r' : '-') +(m.metadata & 0x4 ? 'x' : '-') +(m.metadata & 0x2 ? 'w' : '-') +(m.metadata & 0x1 ? 'r' : '-');(m.metadata & 0x80 ? 'w' : '-') +(m.metadata & 0x40 ? 'r' : '-') +(m.metadata & 0x20 ? 'x' : '-') +(m.metadata & 0x10 ? 'w' : '-') +(m.metadata & 0x8 ? 'r' : '-') +(m.metadata & 0x4 ? 'x' : '-') +(m.metadata & 0x2 ? 'w' : '-') +(m.metadata & 0x1 ? 'r' : '-')); - replacement in ui/src/routes/[user]/[repo]/change/[hash]/Flag.svelte at line 4
export let flag: Flag;let flag_ = bits(flag);const { flag }: { flag: Flag } = $props();let flag_ = $derived(bits(flag)); - replacement in ui/src/routes/[user]/[repo]/change/[hash]/Edge.svelte at line 5
export let edge: Edge;export let deps: Deps;const { edge, deps }: { deps: Deps; edge: Edge } = $props(); - replacement in ui/src/routes/[user]/[repo]/change/[hash]/Contents.svelte at line 2
export let contents: (| {Add: string;}| {Del: string;})[];const {contents}: {contents: (| {Add: string;}| {Del: string;})[];} = $props(); - replacement in ui/src/routes/[user]/[repo]/change/[hash]/Atom.svelte at line 5
export let deps: Deps;export let atom: Atom;const { deps, atom }: { deps: Deps; atom: Atom } = $props(); - replacement in ui/src/routes/[user]/[repo]/change/[hash]/+page.svelte at line 9
export let data: {deps: Deps;authors: { login?: string; key: string }[];hash: string;repo: string;owner: string;header: {message: string;timestamp: string;description: string | null;const {data}: {data: {deps: Deps;authors: { login?: string; key: string }[];hash: string;repo: string;owner: string;header: {message: string;timestamp: string;description: string | null;};hunks: Hunk[]; - replacement in ui/src/routes/[user]/[repo]/change/[hash]/+page.svelte at line 25
hunks: Hunk[];};let date = new Intl.DateTimeFormat('en-US', {dateStyle: 'medium',timeStyle: 'short'}).format(new Date(data.header.timestamp));} = $props();let date = $derived(new Intl.DateTimeFormat('en-US', {dateStyle: 'medium',timeStyle: 'short'}).format(new Date(data.header.timestamp))); - edit in ui/src/routes/[user]/[repo]/change/+page.svelte at line 6
import type { PageData } from './$types'; - replacement in ui/src/routes/[user]/[repo]/change/+page.svelte at line 7
export let data: PageData;const channel = data.channel ? data.channel : 'main';const { data } = $props();const channel = $derived(data.channel ? data.channel : 'main'); - replacement in ui/src/routes/[user]/[repo]/admin/Permission.svelte at line 2
export let login: string;export let owner: string;export let repo: string;export let perms: number;export let everybody: boolean = false;export let n: number;export let token: string;let props: {login: string;owner: string;repo: string;perms: number;everybody?: boolean;n: number;token: string;} = $props(); - replacement in ui/src/routes/[user]/[repo]/admin/Permission.svelte at line 24
<form method="POST" action="/api/admin/{owner}/{repo}/permission"><input type="hidden" name="token" value={token} /><form method="POST" action="/api/admin/{props.owner}/{props.repo}/permission"><input type="hidden" name="token" value={props.token} /> - replacement in ui/src/routes/[user]/[repo]/admin/Permission.svelte at line 32
name={everybody ? '' : 'login'}value={login}disabled={everybody || login == owner} />name={props.everybody ? '' : 'login'}value={props.login}disabled={props.everybody || props.login == props.owner} /> - replacement in ui/src/routes/[user]/[repo]/admin/Permission.svelte at line 43
disabled={login == owner}checked={!!(perms & (1 << k))}id="{n}-{k}" /><label class="label ms-1" for="{n}-{k}"> {label} </label>disabled={props.login == props.owner}checked={!!(props.perms & (1 << k))}id="{props.n}-{k}" /><label class="label ms-1" for="{props.n}-{k}"> {label} </label> - replacement in ui/src/routes/[user]/[repo]/admin/Permission.svelte at line 51
<button class="btn btn-sm btn-secondary" disabled={login == owner}>Ok</button><button class="btn btn-sm btn-secondary" disabled={props.login == props.owner}>Ok</button> - replacement in ui/src/routes/[user]/[repo]/admin/ColorPicker.svelte at line 4
export let owner: string;export let repo: string;export let n: null | number = null;export let tagColor: number = colors[0];export let name = '';export let token: string;export let id: string | null = null;let {owner,repo,n = null,tagColor = $bindable(colors[0]),name = $bindable(''),token,id = null}: {owner: string;repo: string;n: null | number;tagColor: number;name: string;token: string;id: string | null;} = $props(); - replacement in ui/src/routes/[user]/[repo]/admin/ColorPicker.svelte at line 22
let nn = n == null ? '' : n;let nn = $derived(n == null ? '' : n); - replacement in ui/src/routes/[user]/[repo]/admin/+page.svelte at line 8
export let data: PageData;let newTagName = '';let newTagColor = colors[0];const { data } = $props();let newTagName = $state('');let newTagColor = $state(colors[0]); - replacement in ui/src/routes/[user]/[repo]/admin/+page.svelte at line 13
<div class="p-3"><div class="p-3 pb-10"> - replacement in ui/src/routes/[user]/[repo]/Tabs.svelte at line 2
import { page } from '$app/state';export let user: string;export let repo: string;export let login: string | undefined = page.data.login;export let channel: string | undefined = undefined;export let active: 'tree' | 'changes' | 'tags' | 'discussion' | 'ci' | 'admin';const {user,repo,login,channel,active}: {user: string;repo: string;login?: string;channel?: string;active: 'tree' | 'changes' | 'tags' | 'discussion' | 'ci' | 'jobs' | 'admin';} = $props(); - edit in ui/src/routes/[user]/[repo]/Tabs.svelte at line 29
<li class="tab{active == 'jobs' ? ' tab-active' : ''}"><a href="/{user}/{repo}/jobs"><i class="bi bi-cog"></i> Jobs</a></li> - replacement in ui/src/routes/[user]/[repo]/Nav.svelte at line 2
export let user = '';export let repo = '';export let path: { basename: string; pos: string }[] = [];export let link = false;const {user,repo,path,link = false}: {user: string;repo: string;path: { basename: string; pos: string }[];link: boolean;} = $props(); - replacement in ui/src/routes/[user]/[repo]/ChannelBar.svelte at line 5
export let owner: string;export let repo: string;export let channel: string;export let channels: string[];export let can_delete: boolean = false;export let token: string;export let tab: string;let {owner,repo,channel = $bindable(),channels,can_delete = false,token,tab}: {owner: string;repo: string;channel: string;channels: string[];can_delete: boolean;token: string;tab: string;} = $props(); - replacement in ui/src/routes/[user]/[repo]/ChannelBar.svelte at line 43
on:change={change}onchange={change} - replacement in ui/src/routes/[user]/+page.svelte at line 3
export let data: PageData;const { data } = $props(); - replacement in ui/src/routes/+layout.svelte at line 3
import '../app.css';import '../app.scss';const { children } = $props(); - replacement in ui/src/routes/+layout.svelte at line 39
<slot />{@render children()} - file addition: app.scss[2.2255]
@import 'tailwindcss';@plugin "daisyui";@plugin "@iconify/tailwind4";@layer base {h1 {font-size: var(--text-2xl) !important;}h2 {font-size: var(--text-xl) !important;}h3 {font-size: var(--text-l) !important;}} - replacement in ui/pnpm-lock.yaml at line 13
version: 5.5.4(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(typescript@6.0.3)(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))version: 5.5.4(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(typescript@6.0.3)(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))'@sveltejs/vite-plugin-svelte':specifier: ^7.0.0version: 7.0.0(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)) - replacement in ui/pnpm-lock.yaml at line 50
version: 2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(typescript@6.0.3)(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6))version: 2.57.1(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(typescript@6.0.3)(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)) - replacement in ui/pnpm-lock.yaml at line 677
'@sveltejs/vite-plugin-svelte-inspector@5.0.2':resolution: {integrity: sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==}'@sveltejs/vite-plugin-svelte@7.0.0':resolution: {integrity: sha512-ILXmxC7HAsnkK2eslgPetrqqW1BKSL7LktsFgqzNj83MaivMGZzluWq32m25j2mDOjmSKX7GGWahePhuEs7P/g==} - replacement in ui/pnpm-lock.yaml at line 681
'@sveltejs/vite-plugin-svelte': ^6.0.0-next.0svelte: ^5.0.0vite: ^6.3.0 || ^7.0.0svelte: ^5.46.4vite: ^8.0.0-beta.7 || ^8.0.0 - edit in ui/pnpm-lock.yaml at line 684
'@sveltejs/vite-plugin-svelte@6.2.4':resolution: {integrity: sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==}engines: {node: ^20.19 || ^22.12 || >=24}peerDependencies:svelte: ^5.0.0vite: ^6.3.0 || ^7.0.0 - replacement in ui/pnpm-lock.yaml at line 2174
'@sveltejs/adapter-node@5.5.4(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(typescript@6.0.3)(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))':'@sveltejs/adapter-node@5.5.4(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(typescript@6.0.3)(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))': - replacement in ui/pnpm-lock.yaml at line 2179
'@sveltejs/kit': 2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(typescript@6.0.3)(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6))'@sveltejs/kit': 2.57.1(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(typescript@6.0.3)(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)) - replacement in ui/pnpm-lock.yaml at line 2182
'@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(typescript@6.0.3)(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6))':'@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(typescript@6.0.3)(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6))': - replacement in ui/pnpm-lock.yaml at line 2186
'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6))'@sveltejs/vite-plugin-svelte': 7.0.0(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)) - edit in ui/pnpm-lock.yaml at line 2201[8.41962]→[4.73801:73802](∅→∅),[4.73801]→[4.73801:73802](∅→∅),[4.73802]→[8.41963:42253](∅→∅),[8.42253]→[4.74008:74026](∅→∅),[4.74008]→[4.74008:74026](∅→∅),[4.74026]→[8.42254:42400](∅→∅),[8.42400]→[4.74130:74148](∅→∅),[4.74130]→[4.74130:74148](∅→∅),[4.74148]→[8.42401:42515](∅→∅)
'@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6))':dependencies:'@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6))obug: 2.1.1svelte: 5.55.4(@typescript-eslint/types@8.59.0)vite: 8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6) - replacement in ui/pnpm-lock.yaml at line 2202
'@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6))':'@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6))': - edit in ui/pnpm-lock.yaml at line 2204
'@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)))(svelte@5.55.4(@typescript-eslint/types@8.59.0))(vite@8.0.10(esbuild@0.27.7)(jiti@2.6.1)(sass@1.77.6)) - edit in ui/package.json at line 42
"@sveltejs/vite-plugin-svelte": "^7.0.0", - edit in replication/src/lib.rs at line 42
},Eof {repo: uuid::Uuid,channel: String, - file addition: 2026-04-22-153751-0000_jobs[2.297727]
- file addition: up.sql[0.15871]
CREATE TABLE jobs(id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(),repo UUID NOT NULL REFERENCES repositories(id),started TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),ended TIMESTAMP WITH TIME ZONE,status INTEGER);UPDATE permissions SET perm = perm | 0x100;GRANT ALL PRIVILEGES ON jobs TO pijul; - file addition: down.sql[0.15871]
DROP TABLE jobs; - edit in api/src/ssh.rs at line 101
channels_changed: HashSet::new(), - edit in api/src/ssh.rs at line 124
pub channels_changed: HashSet<(uuid::Uuid, String)>, - replacement in api/src/ssh.rs at line 542
self,mut self, - edit in api/src/ssh.rs at line 551
}for (repo, channel) in self.channels_changed.drain() {self.config.replicator.handle_update(None,None,None,::replication::Update::Eof {repo,channel,},).await?; - replacement in api/src/ssh.rs at line 825
&chrono::Utc::now(),&jiff::Timestamp::now(), - replacement in api/src/ssh.rs at line 837
sk::expires.eq(key.expires),sk::expires.eq(key.expires.map(jiff_diesel::Timestamp::from)), - edit in api/src/ssh.rs at line 1277
self.channels_changed.insert((id.origin_id(), c.clone())); - edit in api/src/repository/router.rs at line 1
use super::RawVertexBuf;use crate::Config; - edit in api/src/repository/router.rs at line 4
use crate::Config; - replacement in api/src/repository/router.rs at line 5
debug_handler,Json, debug_handler, - edit in api/src/repository/router.rs at line 8
Json, - edit in api/src/repository/router.rs at line 11
Base32, ChannelTxnT, TxnT, TxnTExt, - edit in api/src/repository/router.rs at line 14
Base32, ChannelTxnT, TxnT, TxnTExt, - replacement in api/src/repository/router.rs at line 83
} else if txn.channels("")?.is_empty() && (pos.channel.as_deref() == Some("main") || pos.channel.is_none()) {} else if txn.channels("")?.is_empty()&& (pos.channel.as_deref() == Some("main") || pos.channel.is_none()){ - edit in api/src/repository/router.rs at line 165
}#[derive(Debug)]struct RawVertexBuf {out: Vec<u8>, - replacement in api/src/repository/mod.rs at line 4
use diesel::{ExpressionMethods, OptionalExtension, QueryDsl,};use diesel::{ExpressionMethods, OptionalExtension, QueryDsl}; - edit in api/src/repository/mod.rs at line 17
pub mod admin; - edit in api/src/repository/mod.rs at line 20
pub mod admin; - edit in api/src/repository/mod.rs at line 25
}#[derive(Debug)]pub struct RawVertexBuf {pub out: Vec<u8>, - replacement in api/src/repository/mod.rs at line 240
.await.optional()?.await.optional()? - replacement in api/src/repository/mod.rs at line 253
Err(crate::Error::Permissions {required, got})Err(crate::Error::Permissions { required, got }) - replacement in api/src/repository/mod.rs at line 261
diesel::dsl::sql::<Bool>("EXISTS (SELECT 1 FROM permissions WHERE permissions.user_id = '00000000-0000-0000-0000-000000000000' AND permissions.repo_id = repositories.id)")diesel::dsl::sql::<Bool>("EXISTS (SELECT 1 FROM permissions WHERE permissions.user_id = '00000000-0000-0000-0000-000000000000' AND permissions.repo_id = repositories.id)",) - replacement in api/src/repository/mod.rs at line 299
.get_result::<(uuid::Uuid,i64,String,Option<String>,i64,bool,bool,)>(db).get_result::<(uuid::Uuid, i64, String, Option<String>, i64, bool, bool)>(db) - replacement in api/src/repository/changestore.rs at line 72
debug!("cache size = {:?}", cache.len());trace!("cache size = {:?}", cache.len()); - replacement in api/src/repository/changestore.rs at line 79
debug!("cache does not contain {:?} {:?}", change, h);trace!("cache does not contain {:?} {:?}", change, h); - replacement in api/src/replication.rs at line 3
use libpijul::pristine::sanakirja::MutTxn;use libpijul::{ChannelMutTxnT, MutTxnT, MutTxnTExt, TxnT, TxnTExt};use diesel::{ExpressionMethods, QueryDsl};use diesel_async::RunQueryDsl;use libpijul::changestore::ChangeStore;use libpijul::fs::FsErrorC;use libpijul::output::{FileError, OutputError};use libpijul::pristine::sanakirja::MutTxn0;use libpijul::pristine::sanakirja::SanakirjaError;use libpijul::pristine::{ForkError, TreeErr, TxnErr};use libpijul::{ApplyError, ArcTxn, ChannelMutTxnT, ChannelRef, MutTxnT, MutTxnTExt, TxnT, TxnTExt,UnrecordError,};use serde_derive::*; - edit in api/src/replication.rs at line 18
use tokio::fs::OpenOptions;use tokio::io::AsyncWriteExt; - edit in api/src/replication.rs at line 24
ci: crate::config_file::CiConfig,jobs: crate::config::Jobs, - edit in api/src/replication.rs at line 28
builders: std::sync::Arc<tokio::sync::Semaphore>, - replacement in api/src/replication.rs at line 32
pub fn new(locks: RepositoryLocks, db: crate::config::Db) -> Self {H { locks, db }pub fn new(ci: crate::config_file::CiConfig,jobs: crate::config::Jobs,locks: RepositoryLocks,db: crate::config::Db,builders: std::sync::Arc<tokio::sync::Semaphore>,) -> Self {H {ci,jobs,locks,db,builders,} - edit in api/src/replication.rs at line 48
use libpijul::pristine::sanakirja::SanakirjaError;use libpijul::pristine::{ForkError, TxnErr};use libpijul::{ApplyError, UnrecordError}; - edit in api/src/replication.rs at line 57
#[error(transparent)]Tree(#[from] TreeErr<SanakirjaError>),#[error(transparent)]File(#[from] FileError<crate::repository::changestore::Error, MutTxn0>), - edit in api/src/replication.rs at line 62
Fs(#[from] FsErrorC<crate::repository::changestore::Error, MutTxn0>),#[error(transparent)] - replacement in api/src/replication.rs at line 66
Unrec(#[from] UnrecordError<crate::repository::changestore::Error, MutTxn<()>>),Unrec(#[from] UnrecordError<crate::repository::changestore::Error, std::io::Error, MutTxn0>),#[error(transparent)]Apply(#[from] ApplyError<crate::repository::changestore::Error, MutTxn0>), - replacement in api/src/replication.rs at line 70
Apply(#[from] ApplyError<crate::repository::changestore::Error, MutTxn<()>>),Output(#[from] OutputError<crate::repository::changestore::Error, MutTxn0, std::io::Error>), - edit in api/src/replication.rs at line 75
#[error(transparent)]Utf8(#[from] std::string::FromUtf8Error), - replacement in api/src/replication.rs at line 97
let mut txn = pri.mut_txn_begin()?;let channel = format!("{}_{}", repo, channel);let channel_ = txn.open_or_create_channel(&channel)?;let result =txn.apply_change(&repo_.changes, &mut *channel_.write(), &hash);let txn = pri.arc_txn_begin()?;let channel_ = format!("{}_{}", repo, channel);let mut txn_ = txn.write();let channel_ = txn_.open_or_create_channel(&channel_)?;let mut channel__ = channel_.write();let result = txn_.apply_change(&repo_.changes, &mut *channel__, &hash); - replacement in api/src/replication.rs at line 135
txn.touch_channel(&mut *channel_.write(), None);txn_.touch_channel(&mut *channel__, None);std::mem::drop(channel__);std::mem::drop(txn_); - edit in api/src/replication.rs at line 149
Update::Eof { repo, channel } => {let repo_ = s.locks.get(&repo).await.unwrap();tokio::task::spawn_blocking(move || {let pri = repo_.pristine.blocking_write();let txn = pri.arc_txn_begin()?;let channel = format!("{}_{}", repo, channel);let channel_ = {let mut txn_ = txn.write();txn_.open_or_create_channel(&channel)?};s.deploy(&txn, &channel_, &repo_.changes, repo)?;Ok::<_, Error>(())});} - replacement in api/src/replication.rs at line 175
let result = txn.unrecord(&repo_.changes, &mut channel_, &hash, 0);let result = txn.unrecord(&repo_.changes,&mut channel_,&hash,0,&libpijul::working_copy::sink(),); - edit in api/src/replication.rs at line 275[2.441524]
#[derive(Debug, Deserialize)]struct Config {deployment: Option<String>,}fn get_file<C: ChangeStore<Error = crate::repository::changestore::Error>>(txn: &ArcTxn<libpijul::pristine::sanakirja::MutTxn0>,channel: &ChannelRef<libpijul::pristine::sanakirja::MutTxn0>,changes: &C,path: &str,) -> Result<Option<String>, Error> {let txn_ = txn.read();let channel_ = channel.read();let (pos, is_dir) = txn_.follow_oldest_path(changes, &channel, path)?;if is_dir {return Ok(None);}let mut out = crate::repository::RawVertexBuf { out: Vec::new() };use libpijul::ChannelTxnT;let mut graph = libpijul::alive::retrieve(&*txn_, txn_.graph(&*channel_), pos, false)?;let mut forward = Vec::new();std::mem::drop(channel_);std::mem::drop(txn_);libpijul::alive::output_graph(changes, &txn, &channel, &mut out, &mut graph, &mut forward).map_err(|x| Error::File(x))?;debug!("{:?}", out);Ok(Some(String::from_utf8(out.out)?))}impl H {fn deploy<C: ChangeStore<Error = crate::repository::changestore::Error> + Clone + Send + Sync + 'static,>(&self,txn: &ArcTxn<libpijul::pristine::sanakirja::MutTxn0>,channel: &ChannelRef<libpijul::pristine::sanakirja::MutTxn0>,changes: &C,repo: uuid::Uuid,) -> Result<(), Error> {if let Some(config) = get_file(txn, channel, changes, "pijul.toml")? {debug!("config = {:?}", config);if let Ok(parsed) = toml::from_str::<Config>(&config) {if let Some(depl) = parsed.deployment {let db = self.db.clone();let txn = txn.clone();let channel = channel.clone();let changes = changes.clone();let jobs = self.jobs.clone();let builders = self.builders.clone();let ci = self.ci.clone();tokio::spawn(async move {let permit = builders.acquire().await.unwrap();use crate::db::jobs::dsl as jobs;let id = diesel::insert_into(jobs::jobs).values((jobs::repo.eq(repo),)).returning(jobs::id).get_result::<uuid::Uuid>(&mut db.get().await.unwrap()).await?;let tmp_dir = tempfile::tempdir()?;let wc = libpijul::working_copy::filesystem::FileSystem::from_root(tmp_dir.path(),);tokio::task::spawn_blocking(move || {libpijul::output::output_repository_no_pending(&wc, &changes, &txn, &channel, "", true, None, 1, 0,)?;Ok::<_, Error>(())}).await.unwrap()?;use std::process::Stdio;let (status_tx, status_rx) = tokio::sync::watch::channel(None);let (kill_tx, kill_rx) = tokio::sync::oneshot::channel();let mut cmd = tokio::process::Command::new(tmp_dir.path().join(depl)).current_dir(tmp_dir.path()).stderr(Stdio::piped()).stdout(Stdio::piped()).stdin(Stdio::null()).spawn()?;use tokio::io::AsyncBufReadExt;let stdout = tokio::io::BufReader::new(cmd.stdout.take().unwrap());let mut stdout = stdout.lines();let mut stdout_ok = true;let stderr = tokio::io::BufReader::new(cmd.stderr.take().unwrap());let mut stderr = stderr.lines();jobs.lock().unwrap().insert(id, (kill_tx, status_tx.clone(), status_rx));let mut stderr_ok = true;let mut buf_stdout = String::new();let mut last_stdout = std::time::UNIX_EPOCH;let mut buf_stderr = String::new();let mut last_stderr = std::time::UNIX_EPOCH;let bound = std::time::Duration::from_secs(1);let mut files =if let Some(ref path) = ci.filesystem{Some((OpenOptions::new().append(true).open(&path.join(&format!("{}.stdout", id))).await?,OpenOptions::new().append(true).open(&path.join(&format!("{}.stderr", id))).await?,))} else {None};while stdout_ok || stderr_ok {debug!("stdout || stderr {:?} {:?}",buf_stdout.len(),buf_stderr.len());tokio::select! {line = stdout.next_line(), if stdout_ok => {let n = if let Some(line) = line? {buf_stdout.push_str(&line);buf_stdout.push('\n');line.len()} else {0};if last_stdout.elapsed().unwrap() >= bound || n == 0 {debug!("sending stdout to db {:?} {:?}", buf_stdout.len(), buf_stderr.len());if let Some((ref mut stdout, _)) = files {stdout.write_all(buf_stdout.as_bytes()).await?;}buf_stdout.clear();debug!("stdout/stderr {:?} {:?}", buf_stdout.len(), buf_stderr.len());last_stdout = std::time::SystemTime::now();}if n == 0 {stdout_ok = false}}line = stderr.next_line(), if stderr_ok => {let n = if let Some(line) = line ?{buf_stderr.push_str(&line);buf_stderr.push('\n');line.len()} else {0};if last_stderr.elapsed().unwrap() >= bound || n == 0 {debug!("sending stderr to db {:?}", buf_stderr.len());if let Some((_, ref mut stderr)) = files {stderr.write_all(buf_stderr.as_bytes()).await?;}buf_stderr.clear();last_stderr = std::time::SystemTime::now();}debug!("{:?}", buf_stderr.len());if n == 0 {stderr_ok = false}}}}let status = tokio::select! {status = cmd.wait() => {status?.code()}_ = kill_rx => {cmd.kill().await?;None}};debug!("process exited with {:?}", status);debug!("stderr {}", buf_stderr);debug!("stdout {}", buf_stdout);let now = chrono::Utc::now();diesel::update(jobs::jobs.find(id)).set((jobs::status.eq(status), jobs::ended.eq(&now))).execute(&mut db.get().await.unwrap()).await?;status_tx.send(Some((now, status))).unwrap();jobs.lock().unwrap().remove(&id);std::mem::drop(permit);Ok::<_, Error>(())});}}} else {debug!("No pijul.toml");}Ok(())}} - replacement in api/src/proxy.rs at line 12
t: Option<&str>,_t: Option<&str>, - edit in api/src/permissions.rs at line 18
const READ_JOBS = 0x100;const WRITE_JOBS = 0x200; - replacement in api/src/permissions.rs at line 26
Perm::READ | Perm::CREATE_DISCUSSIONPerm::READ | Perm::CREATE_DISCUSSION | Perm::READ_JOBS - replacement in api/src/main.rs at line 2
debug_handler,Json, Router, debug_handler, - edit in api/src/main.rs at line 7
Json, Router, - edit in api/src/main.rs at line 31
mod jobs; - replacement in api/src/main.rs at line 74
.allow_origin([config_file.origin.parse::<http::HeaderValue>().unwrap(),]).allow_origin([config_file.origin.parse::<http::HeaderValue>().unwrap()]) - replacement in api/src/main.rs at line 76
/*let cors = CorsLayer::new().allow_origin(tower_http::cors::AllowOrigin::any());*/ - replacement in api/src/main.rs at line 80
let handler = crate::replication::H::new(repo_locks.clone(), config.db.clone());let handler = crate::replication::H::new(config.ci.clone(),config.jobs.clone(),repo_locks.clone(),config.db.clone(),config.builders.clone(),); - edit in api/src/main.rs at line 130
let headers = req.headers(); - edit in api/src/main.rs at line 186
use axum::handler::HandlerWithoutStateExt; - replacement in api/src/main.rs at line 230
async fn logout(State(config): State<Config>, jar: SignedCookieJar) -> (SignedCookieJar, Redirect) {async fn logout(State(_config): State<Config>,jar: SignedCookieJar,) -> (SignedCookieJar, Redirect) { - replacement in api/src/main.rs at line 353
let (id, login) = if let Some((id, login)) = get_user_login(&jar, &config).await? {(id, Some(login))let login = if let Some((_, login)) = get_user_login(&jar, &config).await? {Some(login) - replacement in api/src/main.rs at line 356
(uuid::Uuid::nil(), None)None - edit in api/src/main.rs at line 420
.nest("/job", jobs::router()) - replacement in api/src/main.rs at line 557
libpijul::pristine::sanakirja::GenericTxn<sanakirja::MutTxn<Arc<sanakirja::Env>, ()>>,libpijul::pristine::sanakirja::GenericTxn<sanakirja::MutTxn<Arc<sanakirja::Env>>>, - edit in api/src/main.rs at line 559
std::io::Error, - replacement in api/src/main.rs at line 567
libpijul::pristine::sanakirja::GenericTxn<sanakirja::MutTxn<Arc<sanakirja::Env>, ()>>,libpijul::pristine::sanakirja::GenericTxn<sanakirja::MutTxn<Arc<sanakirja::Env>>>, - edit in api/src/main.rs at line 615
#[error("Not found")]NotFound, - edit in api/src/main.rs at line 663
Error::Forbidden => (StatusCode::FORBIDDEN, "{}").into_response(),Error::NotFound => (StatusCode::NOT_FOUND, "{}").into_response(), - file addition: jobs.rs[2.319271]
use crate::permissions::Perm;use crate::{Config, get_user_login};use axum::{Router,extract::ws::{WebSocket, WebSocketUpgrade},extract::{Json, Path, State},response::Response,routing::{any, get},};use axum_extra::extract::SignedCookieJar;use diesel::{BoolExpressionMethods, ExpressionMethods, NullableExpressionMethods, OptionalExtension,QueryDsl, Queryable, QueryableByName, Selectable, SelectableHelper,};use diesel_async::RunQueryDsl;use futures::StreamExt;use inotify::WatchMask;use serde_derive::*;use tokio::io::{AsyncReadExt, AsyncSeekExt};use tracing::*;pub fn router() -> Router<Config> {Router::new().route("/{owner}/{repo}", get(list_jobs)).route("/{owner}/{repo}/{job_id}", get(job)).route("/{owner}/{repo}/{job_id}/ws", any(ws_handler))}#[derive(Debug, Deserialize)]pub struct JobPath {owner: String,repo: String,job_id: uuid::Uuid,}#[derive(Debug, Deserialize)]pub struct JobsPath {owner: String,repo: String,}#[derive(Debug, Selectable, Queryable, QueryableByName, Serialize)]#[diesel(check_for_backend(diesel::pg::Pg))]#[diesel(table_name = crate::db::jobs::dsl)]struct Job {id: uuid::Uuid,started: chrono::DateTime<chrono::Utc>,ended: Option<chrono::DateTime<chrono::Utc>>,status: Option<i32>,}#[derive(Debug, Serialize)]pub struct Jobs {login: Option<String>,jobs: Vec<Job>,}pub async fn list_jobs(State(config): State<Config>,jar: SignedCookieJar,Path(path): Path<JobsPath>,) -> Result<Json<Jobs>, crate::Error> {let (uid, login) = if let Some((a, b)) = get_user_login(&jar, &config).await? {(Some(a), Some(b))} else {(None, None)};let mut db = config.db.get().await?;use crate::db::jobs::dsl as jobs;use crate::db::repositories::dsl as repos;use crate::db::users::dsl as users;Ok(Json(Jobs {login,jobs: repos::repositories.inner_join(jobs::jobs).inner_join(users::users).filter(users::login.eq(path.owner)).filter(repos::name.eq(path.repo)).filter(repos::owner.nullable().eq(uid).or(crate::has_permissions!(uid.unwrap_or(uuid::Uuid::nil()),repos::id,Perm::READ_JOBS.bits()))).select(Job::as_select()).order_by(jobs::started.desc()).get_results::<Job>(&mut db).await?,}))}#[derive(Debug, Serialize)]pub struct Job_ {login: Option<String>,#[serde(flatten)]job: Job,}pub async fn job(State(config): State<Config>,jar: SignedCookieJar,Path(path): Path<JobPath>,) -> Result<Json<Job_>, crate::Error> {let (uid, login) = if let Some((a, b)) = get_user_login(&jar, &config).await? {(Some(a), Some(b))} else {(None, None)};let mut db = config.db.get().await?;use crate::db::jobs::dsl as jobs;use crate::db::repositories::dsl as repos;use crate::db::users::dsl as users;if let Some(job) = repos::repositories.inner_join(jobs::jobs).inner_join(users::users).filter(users::login.eq(path.owner)).filter(repos::name.eq(path.repo)).filter(repos::owner.nullable().eq(uid).or(crate::has_permissions!(uid.unwrap_or(uuid::Uuid::nil()),repos::id,Perm::READ_JOBS.bits()))).filter(jobs::id.eq(path.job_id)).select(Job::as_select()).order_by(jobs::started.desc()).get_result::<Job>(&mut db).await.optional()?{Ok(Json(Job_ { login, job }))} else {Err(crate::Error::NotFound)}}pub async fn ws_handler(State(config): State<Config>,Path(path): Path<JobPath>,ws: WebSocketUpgrade,) -> Response {ws.on_upgrade(move |socket| handle_socket(config, path.job_id, socket))}#[derive(Debug, Deserialize, Serialize)]enum Msg<'a> {State {stdout: usize,stderr: usize,},Chunk {channel: i32,offset: usize,content: &'a str,},Status {ended: chrono::DateTime<chrono::Utc>,status: Option<i32>,},}async fn handle_socket(config: Config, id: uuid::Uuid, mut socket: WebSocket) {let mut remote_stdout = 0;let mut remote_stderr = 0;let Some(mut status) = config.jobs.lock().unwrap().get(&id).map(|(_, _, w)| w.clone())else {debug!("closing");if let Some((a, b, c)) = send_all(&config, id, &mut remote_stdout, &mut remote_stderr).await.unwrap(){socket.send(a.into()).await.unwrap_or(());socket.send(b.into()).await.unwrap_or(());if let Some(c) = c {socket.send(c.into()).await.unwrap_or(());}}return;};let mut status_ok = true;let mut notify_buffer = [0; 1024];let mut notify = if let Some(ref path) = config.ci.filesystem{let inotify = inotify::Inotify::init().expect("Error while initializing inotify instance");let mut w = inotify.watches();w.add(&path.join(&format!("{}.stdout", id)),WatchMask::MODIFY | WatchMask::CLOSE,).unwrap();w.add(&path.join(&format!("{}.stderr", id)),WatchMask::MODIFY | WatchMask::CLOSE,).unwrap();inotify.into_event_stream(&mut notify_buffer).unwrap()} else {inotify::Inotify::init().unwrap().into_event_stream(&mut notify_buffer).unwrap()};let mut stdout = String::new();let mut stderr = String::new();loop {debug!("waiting job");tokio::select! {_ = notify.next() => {if let Some((a, b)) = send_remaining(&config, id, &mut remote_stdout, &mut remote_stderr, &mut stdout, &mut stderr).await.unwrap(){socket.send(a.into()).await.unwrap_or(());socket.send(b.into()).await.unwrap_or(());}}x = status.changed(), if status_ok => {debug!("status {:?}", x);if x.is_err() {status_ok = false}let status = status.borrow_and_update().clone();debug!("status {:?}", status);if let Some((ended, status)) = status {socket.send(serde_json::to_string(&Msg::Status {ended,status}).unwrap().into()).await.unwrap_or(());} else {debug!("Nothing to send");}},else => break}}}async fn send_all(config: &Config,id: uuid::Uuid,remote_stdout: &mut usize,remote_stderr: &mut usize,) -> Result<Option<(String, String, Option<String>)>, crate::Error> {use crate::db::jobs::dsl as jobs;if let Some((ended, status)) = jobs::jobs.find(id).select((jobs::ended, jobs::status)).get_result::<(Option<chrono::DateTime<chrono::Utc>>, Option<i32>)>(&mut config.db.get().await.unwrap(),).await.optional().unwrap(){let mut stdout = String::new();let mut stderr = String::new();debug!("send_all {:?} {:?}", stdout.len(), stderr.len());if let Some(ref path) = config.ci.filesystem {let mut outf = tokio::fs::File::open(&path.join(&format!("{}.stdout", id))).await?;let mut errf = tokio::fs::File::open(&path.join(&format!("{}.stderr", id))).await?;outf.seek(std::io::SeekFrom::Start(*remote_stdout as u64)).await?;errf.seek(std::io::SeekFrom::Start(*remote_stderr as u64)).await?;outf.read_to_string(&mut stdout).await?;errf.read_to_string(&mut stderr).await?;}*remote_stdout = stdout.len();*remote_stderr = stderr.len();Ok(Some((serde_json::to_string(&Msg::Chunk {channel: 0,offset: 0,content: &stdout,}).unwrap(),serde_json::to_string(&Msg::Chunk {channel: 1,offset: 0,content: &stderr,}).unwrap(),ended.map(|ended| serde_json::to_string(&Msg::Status { ended, status }).unwrap()),)))} else {Ok(None)}}async fn send_remaining(config: &Config,id: uuid::Uuid,remote_stdout: &mut usize,remote_stderr: &mut usize,stdout: &mut String,stderr: &mut String,) -> Result<Option<(String, String)>, crate::Error> {if let Some(ref path) = config.ci.filesystem {let mut outf = tokio::fs::File::open(&path.join(&format!("{}.stdout", id))).await?;let mut errf = tokio::fs::File::open(&path.join(&format!("{}.stderr", id))).await?;outf.seek(std::io::SeekFrom::Start(*remote_stdout as u64)).await?;errf.seek(std::io::SeekFrom::Start(*remote_stderr as u64)).await?;outf.read_to_string(stdout).await?;errf.read_to_string(stderr).await?;Ok(Some((serde_json::to_string(&Msg::Chunk {channel: 0,offset: 0,content: &stdout,}).unwrap(),serde_json::to_string(&Msg::Chunk {channel: 1,offset: 0,content: &stderr,}).unwrap(),)))} else {Ok(None)}} - replacement in api/src/discussions/mod.rs at line 3
use crate::{fallback, get_user_login, get_user_login_email, get_user_login_email_strict, Config};use crate::repository::TreePath;use crate::{Config, fallback, get_user_login, get_user_login_email, get_user_login_email_strict}; - replacement in api/src/discussions/mod.rs at line 6
debug_handler,Form, Json, Router, debug_handler, - edit in api/src/discussions/mod.rs at line 10
Form, Json, Router, - edit in api/src/discussions/mod.rs at line 11
use axum_extra::TypedHeader; - edit in api/src/discussions/mod.rs at line 13
use axum_extra::TypedHeader; - edit in api/src/discussions/mod.rs at line 18
use diesel_async::AsyncPgConnection; - edit in api/src/discussions/mod.rs at line 20
use diesel_async::AsyncPgConnection; - edit in api/src/discussions/mod.rs at line 21
use crate::repository::TreePath; - edit in api/src/discussions/mod.rs at line 23
use libpijul::Base32; - edit in api/src/discussions/mod.rs at line 25
use libpijul::Base32; - edit in api/src/db.rs at line 125
}}diesel::table! {use diesel::sql_types::*;use diesel::pg::sql_types::*;use crate::{Keyalgorithm};jobs (id) {id -> Uuid,repo -> Uuid,started -> Timestamptz,ended -> Nullable<Timestamptz>,status -> Nullable<Int4>, - edit in api/src/db.rs at line 322
diesel::joinable!(jobs -> repositories (repo)); - edit in api/src/db.rs at line 343
jobs, - edit in api/src/config_file.rs at line 2
use std::path::PathBuf; - edit in api/src/config_file.rs at line 8
}#[derive(Default, Debug, Clone, Serialize, Deserialize)]pub struct CiConfig {pub filesystem: Option<PathBuf>, - edit in api/src/config_file.rs at line 57
pub ci: CiConfigFile, - edit in api/src/config_file.rs at line 67
pub ci: CiConfig, - edit in api/src/config_file.rs at line 90
pub struct CiConfigFile {pub port: u16,pub key_path: String,pub public_key_path: Option<String>,pub timeout_secs: u64,pub public_keys: Vec<String>,pub log_path: String,pub tarball_path: String,}#[derive(Debug, Default, Serialize, Deserialize)] - edit in api/src/config.rs at line 77
pub builders: std::sync::Arc<tokio::sync::Semaphore>,pub ci: crate::config_file::CiConfig,pub jobs: Jobs, - edit in api/src/config.rs at line 83
pub type Jobs = Arc<Mutex<HashMap<uuid::Uuid,(tokio::sync::oneshot::Sender<()>,tokio::sync::watch::Sender<Option<(chrono::DateTime<chrono::Utc>, Option<i32>)>>,tokio::sync::watch::Receiver<Option<(chrono::DateTime<chrono::Utc>, Option<i32>)>>,),>,>,>; - edit in api/src/config.rs at line 282
builders: Arc::new(tokio::sync::Semaphore::new(1)),ci: config_file.ci.clone(),jobs: Arc::new(Mutex::new(HashMap::new())), - replacement in api/src/change/list.rs at line 43
date: chrono::DateTime<chrono::Utc>,date: jiff::Timestamp, - replacement in api/src/auth.rs at line 3
debug_handler,Form, debug_handler, - edit in api/src/auth.rs at line 6
Form, - replacement in api/src/auth.rs at line 10
sql_types::Bool, BoolExpressionMethods, ExpressionMethods, OptionalExtension, QueryDsl,BoolExpressionMethods, ExpressionMethods, OptionalExtension, QueryDsl, sql_types::Bool, - replacement in api/src/auth.rs at line 14
use rand::{rng, Rng};use rand::{Rng, rng}; - replacement in api/src/auth.rs at line 271
#[error("This user is inactive. Please contact <a href=\"mailto:support@pijul.org\">support@pijul.org</a> to resolve this situation.")]#[error("This user is inactive. Please contact <a href=\"mailto:support@pijul.org\">support@pijul.org</a> to resolve this situation.")] - replacement in api/Cargo.toml at line 9
axum = { version = "0.8.7", features = ["macros"] }axum = { version = "0.8.7", features = ["macros", "ws"] } - edit in api/Cargo.toml at line 39
inotify = "0.11.1" - edit in api/Cargo.toml at line 41
jiff = "0.2.16"jiff-diesel = { version = "0.1.3", features = ["postgres"] } - replacement in api/Cargo.toml at line 44
libpijul = "1.0.0-beta.10"libc = "0.2.185"libpijul = "1.0.0-beta.11" - replacement in api/Cargo.toml at line 62
sanakirja = "1.4.3"sanakirja = "2.0.0-beta" - edit in Cargo.nix at line 8071
};"inotify" = rec {crateName = "inotify";version = "0.11.1";edition = "2018";sha256 = "16fiffnqhfdwzgrv3wcnaih0a9xbx1a44nma1yn5idr83apkwnxx";authors = ["Hanno Braun <mail@hannobraun.de>""Félix Saparelli <me@passcod.name>""Cristian Kubis <cristian.kubis@tsunix.de>""Frank Denis <github@pureftpd.org>"];dependencies = [{name = "bitflags";packageId = "bitflags 2.10.0";}{name = "futures-core";packageId = "futures-core";optional = true;}{name = "inotify-sys";packageId = "inotify-sys";}{name = "libc";packageId = "libc";}{name = "tokio";packageId = "tokio";optional = true;features = [ "net" ];}];devDependencies = [{name = "tokio";packageId = "tokio";features = [ "macros" "rt-multi-thread" "time" ];}];features = {"default" = [ "stream" ];"futures-core" = [ "dep:futures-core" ];"stream" = [ "futures-core" "tokio" ];"tokio" = [ "dep:tokio" ];};resolvedDefaultFeatures = [ "default" "futures-core" "stream" "tokio" ];};"inotify-sys" = rec {crateName = "inotify-sys";version = "0.1.5";edition = "2015";sha256 = "1syhjgvkram88my04kv03s0zwa66mdwa5v7ddja3pzwvx2sh4p70";libName = "inotify_sys";authors = ["Hanno Braun <hb@hannobraun.de>"];dependencies = [{name = "libc";packageId = "libc";}]; - edit in Cargo.nix at line 9948
name = "inotify";packageId = "inotify";}{ - edit in Cargo.lock at line 320
"base64 0.22.1", - edit in Cargo.lock at line 339
"sha1", - edit in Cargo.lock at line 342
"tokio-tungstenite", - edit in Cargo.lock at line 595
dependencies = ["serde_core",] - edit in Cargo.lock at line 1236
"subtle","zeroize",][[package]]name = "curve25519-dalek"version = "4.1.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"dependencies = ["cfg-if","cpufeatures","curve25519-dalek-derive","fiat-crypto","rustc_version", - edit in Cargo.lock at line 1257
name = "curve25519-dalek-derive"version = "0.1.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"dependencies = ["proc-macro2","quote","syn 2.0.111",][[package]] - replacement in Cargo.lock at line 1637
"curve25519-dalek","curve25519-dalek 3.2.0", - edit in Cargo.lock at line 1711
[[package]]name = "fiat-crypto"version = "0.2.9"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - replacement in Cargo.lock at line 1793
name = "fs2"version = "0.4.3"name = "fs4"version = "0.6.6" - replacement in Cargo.lock at line 1796
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47" - replacement in Cargo.lock at line 1798
"libc","winapi","rustix 0.38.44","windows-sys 0.48.0", - edit in Cargo.lock at line 2535
name = "imara-diff"version = "0.1.8"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "17d34b7d42178945f775e84bc4c36dde7c1c6cdfea656d3354d009056f2bb3d2"dependencies = ["hashbrown 0.15.5",][[package]] - edit in Cargo.lock at line 2553
][[package]]name = "inotify"version = "0.11.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199"dependencies = ["bitflags 2.10.0","futures-core","inotify-sys","libc","tokio", - edit in Cargo.lock at line 2569
name = "inotify-sys"version = "0.1.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"dependencies = ["libc",][[package]] - edit in Cargo.lock at line 2654
"jiff-tzdb-platform", - edit in Cargo.lock at line 2659
"windows-sys 0.61.2", - edit in Cargo.lock at line 2663
name = "jiff-diesel"version = "0.1.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "d3a17d80f23d2a3872a3aaef87c168a6e0442118e55b463ba7f864f82376eeee"dependencies = ["diesel","jiff",][[package]] - edit in Cargo.lock at line 2681
][[package]]name = "jiff-tzdb"version = "0.1.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076"[[package]]name = "jiff-tzdb-platform"version = "0.1.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"dependencies = ["jiff-tzdb", - replacement in Cargo.lock at line 2738
version = "0.2.177"version = "0.2.185" - replacement in Cargo.lock at line 2740
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" - replacement in Cargo.lock at line 2750
version = "1.0.0-beta.10"version = "1.0.0-beta.11" - replacement in Cargo.lock at line 2752
checksum = "230807b6b791be169a67d951be0a4c3f1a231281cfc2a8caddec6804e188517a"checksum = "f625e90234ef1fd164dfe7980ffb04a3036a08e31e051cc29e6273a0c9a7a7e7" - replacement in Cargo.lock at line 2757
"bitflags 1.3.2","bitflags 2.10.0", - edit in Cargo.lock at line 2762
"cfg-if", - edit in Cargo.lock at line 2763
"chrono", - replacement in Cargo.lock at line 2764
"curve25519-dalek","curve25519-dalek 4.1.3", - replacement in Cargo.lock at line 2774
"lazy_static","imara-diff","jiff", - edit in Cargo.lock at line 2778
"memchr", - replacement in Cargo.lock at line 2779
"parking_lot 0.11.2","parking_lot 0.12.5", - replacement in Cargo.lock at line 2783
"rand 0.8.5","rand 0.9.2", - replacement in Cargo.lock at line 2792
"thiserror 1.0.69","toml 0.5.11","thiserror 2.0.17","toml 0.8.23", - edit in Cargo.lock at line 2869
version = "0.4.15"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"[[package]]name = "linux-raw-sys" - edit in Cargo.lock at line 3163
"inotify", - edit in Cargo.lock at line 3165
"jiff","jiff-diesel", - edit in Cargo.lock at line 3168
"libc", - replacement in Cargo.lock at line 3858
version = "0.1.5"version = "0.2.1" - replacement in Cargo.lock at line 3860
checksum = "498a099351efa4becc6a19c72aa9270598e8fd274ca47052e37455241c88b696"checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" - replacement in Cargo.lock at line 4048
version = "0.5.0"version = "1.0.0-beta.11" - replacement in Cargo.lock at line 4050
checksum = "c2b3743cd80b65d40b9f20b6575016f76d4221754866316ffbf7d7b0d58e7064"checksum = "a794a27eb67b7ae7f940c4b94e186a58a525f87d11de1c1a0e317c9b7aafb163" - replacement in Cargo.lock at line 4054
"regex","syn 1.0.109","syn 2.0.111", - edit in Cargo.lock at line 4742
version = "0.38.44"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"dependencies = ["bitflags 2.10.0","errno","libc","linux-raw-sys 0.4.15","windows-sys 0.52.0",][[package]]name = "rustix" - replacement in Cargo.lock at line 4762
"linux-raw-sys","linux-raw-sys 0.11.0", - replacement in Cargo.lock at line 4833
version = "1.4.3"version = "2.0.0-beta" - replacement in Cargo.lock at line 4835
checksum = "81aaf70d064e2122209f04d01fd91e8908e7a327b516236e1cbc0c3f34ac6d11"checksum = "4fc53a1e7a19d27c070749c0d2717427d1612c9b100274b205dbb438e605e34a" - replacement in Cargo.lock at line 4838
"fs2","fs4", - edit in Cargo.lock at line 4840
"libc", - replacement in Cargo.lock at line 4851
version = "1.4.1"version = "2.0.0-beta" - replacement in Cargo.lock at line 4853
checksum = "8376db34ae3eac6e7bd91168bc638450073b708ce9fb46940de676f552238bf5"checksum = "48a8500bcb79fe605b1433c30e8c5c078810b24dc4d04ffda35e88d712ff16f0" - edit in Cargo.lock at line 5019
][[package]]name = "serde_spanned"version = "0.6.9"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"dependencies = ["serde", - replacement in Cargo.lock at line 5370
"rustix","rustix 1.1.2", - edit in Cargo.lock at line 5676
name = "tokio-tungstenite"version = "0.28.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857"dependencies = ["futures-util","log","tokio","tungstenite",][[package]] - replacement in Cargo.lock at line 5702
version = "0.5.11"version = "0.8.23" - replacement in Cargo.lock at line 5704
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" - edit in Cargo.lock at line 5706
"indexmap", - edit in Cargo.lock at line 5708
"serde_spanned 0.6.9","toml_datetime 0.6.11","toml_edit", - replacement in Cargo.lock at line 5721
"serde_spanned","toml_datetime","serde_spanned 1.0.3","toml_datetime 0.7.3", - edit in Cargo.lock at line 5730
version = "0.6.11"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"dependencies = ["serde",][[package]]name = "toml_datetime" - edit in Cargo.lock at line 5747
name = "toml_edit"version = "0.22.27"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"dependencies = ["indexmap","serde","serde_spanned 0.6.9","toml_datetime 0.6.11","toml_write","winnow",][[package]] - edit in Cargo.lock at line 5768
[[package]]name = "toml_write"version = "0.1.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - edit in Cargo.lock at line 5909
[[package]]name = "tungstenite"version = "0.28.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442"dependencies = ["bytes","data-encoding","http 1.4.0","httparse","log","rand 0.9.2","sha1","thiserror 2.0.17","utf-8",] - edit in Cargo.lock at line 6480
][[package]]name = "windows-sys"version = "0.48.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"dependencies = ["windows-targets 0.48.5", - edit in Cargo.lock at line 6520
version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"dependencies = ["windows_aarch64_gnullvm 0.48.5","windows_aarch64_msvc 0.48.5","windows_i686_gnu 0.48.5","windows_i686_msvc 0.48.5","windows_x86_64_gnu 0.48.5","windows_x86_64_gnullvm 0.48.5","windows_x86_64_msvc 0.48.5",][[package]]name = "windows-targets" - edit in Cargo.lock at line 6565
[[package]]name = "windows_aarch64_gnullvm"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - edit in Cargo.lock at line 6586
version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"[[package]]name = "windows_aarch64_msvc" - edit in Cargo.lock at line 6604
version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"[[package]]name = "windows_i686_gnu" - edit in Cargo.lock at line 6631
[[package]]name = "windows_i686_msvc"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - edit in Cargo.lock at line 6652
version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"[[package]]name = "windows_x86_64_gnu" - edit in Cargo.lock at line 6667
[[package]]name = "windows_x86_64_gnullvm"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - edit in Cargo.lock at line 6685
[[package]]name = "windows_x86_64_msvc"version = "0.48.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - edit in Cargo.lock at line 6709
dependencies = ["memchr",] - replacement in Cargo.lock at line 6846
"rustix","rustix 1.1.2",