Initial attempt at CI/CD

pmeunier
Apr 23, 2026, 7:45 PM
YYJ76Q7V6G7FHSNZ25MU3ZZY5KGXIODBHLWOZUGB4FIKL7PUCNAAC

Dependencies

Change contents

  • edit in ui/vite.config.js at line 7
    [3.41]
    [3.41]
    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
    [2.554]
    [2.554]
    '/recover': {
    target: 'http://127.0.0.1:8001',
    secure: false
    },
  • edit in ui/svelte.config.js at line 3
    [2.2055]
    [2.2055]
    import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
  • replacement in ui/svelte.config.js at line 7
    [2.2108][2.2108:2134]()
    preprocess: [mdsvex()],
    [2.2108]
    [2.2134]
    compilerOptions: {
    runes: true
    },
    preprocess: [
    vitePreprocess({
    script: true,
    style: true
    }),
    mdsvex()
    ],
  • file addition: static (d--r------)
    [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 (----------)
    [2.2255][2.119969:120000](),[2.120000][2.120001:120001]()
    @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
    [2.6894][2.6894:6918]()
    h3 { font-size: 15pt; }
  • edit in ui/src/routes/terms/+page.svx at line 106
    [2.23553][2.23553:23577]()
    h3 { font-size: 15pt; }
  • edit in ui/src/routes/settings/ssh/+page.svelte at line 4
    [2.24492][2.24492:24522]()
    export let data: PageData;
  • replacement in ui/src/routes/settings/ssh/+page.svelte at line 5
    [2.24523][2.24523:24552]()
    console.log('data', data);
    [2.24523]
    [2.24552]
    const { data } = $props();
  • replacement in ui/src/routes/settings/ssh/+page.svelte at line 7
    [2.24553][2.24553:24574]()
    let m: any = null;
    [2.24553]
    [2.24574]
    let m: any = $state(null);
  • replacement in ui/src/routes/settings/account/+page.svelte at line 3
    [2.26292][2.26292:26321]()
    export let data: PageData;
    [2.26292]
    [2.26321]
    const { data } = $props();
  • edit in ui/src/routes/settings/+page.svelte at line 5
    [7.297][2.28782:28812](),[2.28782][2.28782:28812]()
    export let data: PageData;
  • replacement in ui/src/routes/settings/+page.svelte at line 6
    [2.28813][2.28813:28842]()
    console.log('data', data);
    [2.28813]
    [2.28985]
    const { data } = $props();
  • replacement in ui/src/routes/recover/+page.svelte at line 2
    [2.33067][2.33067:33111]()
    export let data: null | { code: string };
    [2.33067]
    [2.33111]
    const { data }: { data: null | { code: string } } = $props();
  • edit in ui/src/routes/helpers.ts at line 3
    [2.53388][2.53388:53432]()
    import { browser } from '$app/environment';
  • replacement in ui/src/routes/helpers.ts at line 12
    [2.53557][5.23:73]()
    export const server = import.meta.env.VITE_SERVER
    [2.53557]
    [2.53654]
    export const server = import.meta.env.VITE_SERVER;
  • replacement in ui/src/routes/[user]/[repo]/tree/[[pos]]/+page.svelte at line 8
    [7.1814][2.56107:56199](),[2.56107][2.56107:56199]()
    export let data: PageData;
    const channel = data.channel.length ? data.channel : 'main';
    [7.1814]
    [2.56199]
    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
    [2.56268]
    [2.56268]
    <link rel="stylesheet" href="/hljs.css" />
  • edit in ui/src/routes/[user]/[repo]/tree/[[pos]]/+page.svelte at line 82
    [2.58295][2.58295:60978]()
    :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 (d--r------)
    [2.54792]
  • file addition: [job] (d--r------)
    [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} />
    <Tabs
    user={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>
    <a
    class="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
    [2.61611][2.61611:61761]()
    export let data: {
    owner: string;
    repo: string;
    login: string;
    unique: number;
    token: string;
    };
    console.log('data', data);
    [2.61611]
    [2.61761]
    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
    [2.64322][2.64322:64592]()
    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;
    };
    [2.64322]
    [2.64592]
    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
    [2.66536][2.66536:66626]()
    export let n: number | null = null;
    export let edit = false;
    export let title = '';
    [2.66536]
    [2.66626]
    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
    [2.67545][2.67545:67568]()
    export let tag: Tag;
    [2.67545]
    [2.67568]
    const { tag }: { tag: Tag } = $props();
  • replacement in ui/src/routes/[user]/[repo]/discussion/Patch.svelte at line 3
    [2.68574][2.68574:69171]()
    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));
    [2.68574]
    [2.69171]
    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
    [2.71408][2.71408:71447]()
    import { assets } from '$app/paths';
  • edit in ui/src/routes/[user]/[repo]/discussion/Comment.svelte at line 4
    [2.71550][2.71550:71950]()
    // 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
    [2.71951][2.71951:72077]()
    export let comment: Comment = {
    author: login,
    id: '',
    timestamp: 0,
    content: '',
    content_html: ''
    };
    [2.71951]
    [2.72077]
    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
    [2.72972][2.72972:73033]()
    on:click={() => (edit = true)}>Edit</button>
    [2.72972]
    [2.73033]
    onclick={() => (edit = true)}>Edit</button>
  • edit in ui/src/routes/[user]/[repo]/discussion/+page.svelte at line 5
    [2.75773][2.75773:75817]()
    import type { PageData } from './$types';
  • replacement in ui/src/routes/[user]/[repo]/discussion/+page.svelte at line 6
    [2.75859][2.75859:75917]()
    export let data: PageData;
    let includeClosed = false;
    [2.75859]
    [2.75917]
    const { data } = $props();
    let includeClosed = $state(false);
  • replacement in ui/src/routes/[user]/[repo]/discussion/+page.svelte at line 25
    [2.76445][2.76445:76474]()
    on:change={() => {
    [2.76445]
    [2.76474]
    onchange={() => {
  • replacement in ui/src/routes/[user]/[repo]/change/[hash]/Pos.svelte at line 3
    [2.80551][2.80551:80611]()
    export let c: Position | Vertex;
    export let deps: Deps;
    [2.80551]
    [2.80611]
    const { c, deps }: { c: Position | Vertex; deps: Deps } = $props();
  • replacement in ui/src/routes/[user]/[repo]/change/[hash]/Pos.svelte at line 5
    [2.80612][2.80612:80717]()
    let d: string | null = null;
    if (deps && deps.hashes[c.change]) {
    d = deps.hashes[c.change];
    }
    [2.80612]
    [2.80717]
    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
    [2.81454][2.81454:81537]()
    export let m: Meta;
    let dir = (m.metadata & 0x200) != 0 ? 'd' : '-';
    let p =
    [2.81454]
    [2.81537]
    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
    [2.81576][2.81576:81875]()
    (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' : '-');
    [2.81576]
    [2.81875]
    (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
    [2.82073][2.82073:82124]()
    export let flag: Flag;
    let flag_ = bits(flag);
    [2.82073]
    [2.82124]
    const { flag }: { flag: Flag } = $props();
    let flag_ = $derived(bits(flag));
  • replacement in ui/src/routes/[user]/[repo]/change/[hash]/Edge.svelte at line 5
    [2.82424][2.82424:82474]()
    export let edge: Edge;
    export let deps: Deps;
    [2.82424]
    [2.82474]
    const { edge, deps }: { deps: Deps; edge: Edge } = $props();
  • replacement in ui/src/routes/[user]/[repo]/change/[hash]/Contents.svelte at line 2
    [2.82665][2.82665:82771]()
    export let contents: (
    | {
    Add: string;
    }
    | {
    Del: string;
    }
    )[];
    [2.82665]
    [2.82771]
    const {
    contents
    }: {
    contents: (
    | {
    Add: string;
    }
    | {
    Del: string;
    }
    )[];
    } = $props();
  • replacement in ui/src/routes/[user]/[repo]/change/[hash]/Atom.svelte at line 5
    [2.83562][2.83562:83612]()
    export let deps: Deps;
    export let atom: Atom;
    [2.83562]
    [2.83612]
    const { deps, atom }: { deps: Deps; atom: Atom } = $props();
  • replacement in ui/src/routes/[user]/[repo]/change/[hash]/+page.svelte at line 9
    [2.85022][2.85022:85258]()
    export let data: {
    deps: Deps;
    authors: { login?: string; key: string }[];
    hash: string;
    repo: string;
    owner: string;
    header: {
    message: string;
    timestamp: string;
    description: string | null;
    [2.85022]
    [2.85258]
    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
    [2.85265][2.85265:85431]()
    hunks: Hunk[];
    };
    let date = new Intl.DateTimeFormat('en-US', {
    dateStyle: 'medium',
    timeStyle: 'short'
    }).format(new Date(data.header.timestamp));
    [2.85265]
    [2.85431]
    } = $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
    [2.95348][2.95348:95392]()
    import type { PageData } from './$types';
  • replacement in ui/src/routes/[user]/[repo]/change/+page.svelte at line 7
    [2.95393][2.95393:95478]()
    export let data: PageData;
    const channel = data.channel ? data.channel : 'main';
    [2.95393]
    [2.95478]
    const { data } = $props();
    const channel = $derived(data.channel ? data.channel : 'main');
  • replacement in ui/src/routes/[user]/[repo]/admin/Permission.svelte at line 2
    [2.100013][2.100013:100217]()
    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;
    [2.100013]
    [2.100217]
    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
    [2.100412][2.100412:100532]()
    <form method="POST" action="/api/admin/{owner}/{repo}/permission">
    <input type="hidden" name="token" value={token} />
    [2.100412]
    [2.100532]
    <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
    [2.100661][2.100661:100773]()
    name={everybody ? '' : 'login'}
    value={login}
    disabled={everybody || login == owner} />
    [2.100661]
    [2.100773]
    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
    [2.100980][2.100980:101157]()
    disabled={login == owner}
    checked={!!(perms & (1 << k))}
    id="{n}-{k}" />
    <label class="label ms-1" for="{n}-{k}"> {label} </label>
    [2.100980]
    [2.101157]
    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
    [2.101220][2.101220:101305]()
    <button class="btn btn-sm btn-secondary" disabled={login == owner}>Ok</button>
    [2.101220]
    [2.101305]
    <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
    [2.101444][2.101444:101671]()
    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;
    [2.101444]
    [2.101671]
    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
    [2.101672][2.101672:101703]()
    let nn = n == null ? '' : n;
    [2.101672]
    [2.101703]
    let nn = $derived(n == null ? '' : n);
  • replacement in ui/src/routes/[user]/[repo]/admin/+page.svelte at line 8
    [2.105242][2.105242:105325]()
    export let data: PageData;
    let newTagName = '';
    let newTagColor = colors[0];
    [2.105242]
    [2.105325]
    const { data } = $props();
    let newTagName = $state('');
    let newTagColor = $state(colors[0]);
  • replacement in ui/src/routes/[user]/[repo]/admin/+page.svelte at line 13
    [2.105336][2.105336:105354]()
    <div class="p-3">
    [2.105336]
    [2.105354]
    <div class="p-3 pb-10">
  • replacement in ui/src/routes/[user]/[repo]/Tabs.svelte at line 2
    [2.108178][2.108178:108381](),[2.108381][2.108381:108463]()
    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';
    [2.108178]
    [2.108463]
    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
    [2.109077]
    [2.109077]
    <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
    [2.109329][2.109329:109465]()
    export let user = '';
    export let repo = '';
    export let path: { basename: string; pos: string }[] = [];
    export let link = false;
    [2.109329]
    [2.109465]
    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
    [2.110460][2.110460:110674]()
    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;
    [2.110460]
    [2.110674]
    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
    [2.111213][2.111213:111242]()
    on:change={change}
    [2.111213]
    [2.111242]
    onchange={change}
  • replacement in ui/src/routes/[user]/+page.svelte at line 3
    [2.114022][2.114022:114051]()
    export let data: PageData;
    [2.114022]
    [2.114051]
    const { data } = $props();
  • replacement in ui/src/routes/+layout.svelte at line 3
    [2.117858][2.117858:117881]()
    import '../app.css';
    [2.117858]
    [2.117881]
    import '../app.scss';
    const { children } = $props();
  • replacement in ui/src/routes/+layout.svelte at line 39
    [2.118474][2.118474:118487]()
    <slot />
    [2.118474]
    [2.118487]
    {@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
    [4.227][8.0:303]()
    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)))
    [4.227]
    [4.446]
    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.0
    version: 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
    [8.884][8.884:1166]()
    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))
    [8.884]
    [4.1400]
    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
    [4.21622][4.21622:21797]()
    '@sveltejs/vite-plugin-svelte-inspector@5.0.2':
    resolution: {integrity: sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==}
    [4.21622]
    [4.21797]
    '@sveltejs/vite-plugin-svelte@7.0.0':
    resolution: {integrity: sha512-ILXmxC7HAsnkK2eslgPetrqqW1BKSL7LktsFgqzNj83MaivMGZzluWq32m25j2mDOjmSKX7GGWahePhuEs7P/g==}
  • replacement in ui/pnpm-lock.yaml at line 681
    [4.21865][4.21865:21967]()
    '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0
    svelte: ^5.0.0
    vite: ^6.3.0 || ^7.0.0
    [4.21865]
    [4.21967]
    svelte: ^5.46.4
    vite: ^8.0.0-beta.7 || ^8.0.0
  • edit in ui/pnpm-lock.yaml at line 684
    [4.21968][4.21968:22252]()
    '@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.0
    vite: ^6.3.0 || ^7.0.0
  • replacement in ui/pnpm-lock.yaml at line 2174
    [4.72399][8.40548:40862]()
    '@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)))':
    [4.72399]
    [4.72629]
    '@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
    [8.41027][8.41027:41315]()
    '@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))
    [8.41027]
    [8.41315]
    '@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
    [4.73037][8.41337:41621]()
    '@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))':
    [4.73037]
    [4.73237]
    '@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
    [4.73348][8.41622:41768]()
    '@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))
    [4.73348]
    [4.73452]
    '@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.1
    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 2202
    [4.74221][8.42516:42658]()
    '@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))':
    [4.74221]
    [4.74321]
    '@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
    [4.74339][8.42659:42953]()
    '@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
    [3.4408]
    [8.58194]
    "@sveltejs/vite-plugin-svelte": "^7.0.0",
  • edit in replication/src/lib.rs at line 42
    [2.293959]
    [2.293959]
    },
    Eof {
    repo: uuid::Uuid,
    channel: String,
  • file addition: 2026-04-22-153751-0000_jobs (d--r------)
    [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
    [2.322674]
    [2.322674]
    channels_changed: HashSet::new(),
  • edit in api/src/ssh.rs at line 124
    [2.323274]
    [2.323274]
    pub channels_changed: HashSet<(uuid::Uuid, String)>,
  • replacement in api/src/ssh.rs at line 542
    [3.9018][3.9018:9032]()
    self,
    [3.9018]
    [3.9032]
    mut self,
  • edit in api/src/ssh.rs at line 551
    [2.338498]
    [2.338498]
    }
    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
    [2.348932][2.348932:348969]()
    &chrono::Utc::now(),
    [2.348932]
    [2.348969]
    &jiff::Timestamp::now(),
  • replacement in api/src/ssh.rs at line 837
    [2.349479][2.349479:349528]()
    sk::expires.eq(key.expires),
    [2.349479]
    [2.349528]
    sk::expires.eq(key.expires.map(jiff_diesel::Timestamp::from)),
  • edit in api/src/ssh.rs at line 1277
    [2.366128]
    [2.366128]
    self.channels_changed.insert((id.origin_id(), c.clone()));
  • edit in api/src/repository/router.rs at line 1
    [2.391778]
    [2.391779]
    use super::RawVertexBuf;
    use crate::Config;
  • edit in api/src/repository/router.rs at line 4
    [2.391809][2.391809:391828]()
    use crate::Config;
  • replacement in api/src/repository/router.rs at line 5
    [2.391840][2.391840:391859]()
    debug_handler,
    [2.391840]
    [2.391859]
    Json, debug_handler,
  • edit in api/src/repository/router.rs at line 8
    [2.391928][2.391928:391938]()
    Json,
  • edit in api/src/repository/router.rs at line 11
    [2.391999]
    [2.391999]
    Base32, ChannelTxnT, TxnT, TxnTExt,
  • edit in api/src/repository/router.rs at line 14
    [2.392076][2.392076:392116]()
    Base32, ChannelTxnT, TxnT, TxnTExt,
  • replacement in api/src/repository/router.rs at line 83
    [2.394024][2.394024:394142]()
    } else if txn.channels("")?.is_empty() && (pos.channel.as_deref() == Some("main") || pos.channel.is_none()) {
    [2.394024]
    [2.394142]
    } 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
    [2.396737][2.396737:396797]()
    }
    #[derive(Debug)]
    struct RawVertexBuf {
    out: Vec<u8>,
  • replacement in api/src/repository/mod.rs at line 4
    [2.397501][2.397501:397570]()
    use diesel::{
    ExpressionMethods, OptionalExtension, QueryDsl,
    };
    [2.397501]
    [2.397570]
    use diesel::{ExpressionMethods, OptionalExtension, QueryDsl};
  • edit in api/src/repository/mod.rs at line 17
    [2.397880]
    [2.397880]
    pub mod admin;
  • edit in api/src/repository/mod.rs at line 20
    [2.397917][2.397917:397932]()
    pub mod admin;
  • edit in api/src/repository/mod.rs at line 25
    [2.398030]
    [2.398030]
    }
    #[derive(Debug)]
    pub struct RawVertexBuf {
    pub out: Vec<u8>,
  • replacement in api/src/repository/mod.rs at line 240
    [2.403992][2.403992:404019]()
    .await.optional()?
    [2.403992]
    [2.404019]
    .await
    .optional()?
  • replacement in api/src/repository/mod.rs at line 253
    [2.404323][2.404323:404412]()
    Err(crate::Error::Permissions {
    required, got
    })
    [2.404323]
    [2.404412]
    Err(crate::Error::Permissions { required, got })
  • replacement in api/src/repository/mod.rs at line 261
    [2.404551][2.404551:404727]()
    diesel::dsl::sql::<Bool>("EXISTS (SELECT 1 FROM permissions WHERE permissions.user_id = '00000000-0000-0000-0000-000000000000' AND permissions.repo_id = repositories.id)")
    [2.404551]
    [2.404727]
    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
    [2.405559][2.405559:405740]()
    .get_result::<(
    uuid::Uuid,
    i64,
    String,
    Option<String>,
    i64,
    bool,
    bool,
    )>(db)
    [2.405559]
    [2.405740]
    .get_result::<(uuid::Uuid, i64, String, Option<String>, i64, bool, bool)>(db)
  • replacement in api/src/repository/changestore.rs at line 72
    [2.412957][2.412957:413007]()
    debug!("cache size = {:?}", cache.len());
    [2.412957]
    [2.413007]
    trace!("cache size = {:?}", cache.len());
  • replacement in api/src/repository/changestore.rs at line 79
    [2.413307][2.413307:413378]()
    debug!("cache does not contain {:?} {:?}", change, h);
    [2.413307]
    [2.413378]
    trace!("cache does not contain {:?} {:?}", change, h);
  • replacement in api/src/replication.rs at line 3
    [2.431888][2.431888:431999]()
    use libpijul::pristine::sanakirja::MutTxn;
    use libpijul::{ChannelMutTxnT, MutTxnT, MutTxnTExt, TxnT, TxnTExt};
    [2.431888]
    [2.431999]
    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
    [2.432036]
    [2.432036]
    use tokio::fs::OpenOptions;
    use tokio::io::AsyncWriteExt;
  • edit in api/src/replication.rs at line 24
    [2.432085]
    [2.432085]
    ci: crate::config_file::CiConfig,
    jobs: crate::config::Jobs,
  • edit in api/src/replication.rs at line 28
    [2.432140]
    [2.432140]
    builders: std::sync::Arc<tokio::sync::Semaphore>,
  • replacement in api/src/replication.rs at line 32
    [2.432152][2.432152:432248]()
    pub fn new(locks: RepositoryLocks, db: crate::config::Db) -> Self {
    H { locks, db }
    [2.432152]
    [2.432248]
    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
    [2.432256][2.432256:432257](),[2.432257][2.432257:432396]()
    use libpijul::pristine::sanakirja::SanakirjaError;
    use libpijul::pristine::{ForkError, TxnErr};
    use libpijul::{ApplyError, UnrecordError};
  • edit in api/src/replication.rs at line 57
    [2.432621]
    [2.432621]
    #[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
    [2.432647]
    [2.432647]
    Fs(#[from] FsErrorC<crate::repository::changestore::Error, MutTxn0>),
    #[error(transparent)]
  • replacement in api/src/replication.rs at line 66
    [2.432718][2.432718:432803]()
    Unrec(#[from] UnrecordError<crate::repository::changestore::Error, MutTxn<()>>),
    [2.432718]
    [2.432803]
    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
    [2.432829][2.432829:432911]()
    Apply(#[from] ApplyError<crate::repository::changestore::Error, MutTxn<()>>),
    [2.432829]
    [2.432911]
    Output(#[from] OutputError<crate::repository::changestore::Error, MutTxn0, std::io::Error>),
  • edit in api/src/replication.rs at line 75
    [2.433017]
    [2.433017]
    #[error(transparent)]
    Utf8(#[from] std::string::FromUtf8Error),
  • replacement in api/src/replication.rs at line 97
    [2.433736][2.433736:433982](),[2.433982][7.5692:5785]()
    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);
    [2.433736]
    [2.434079]
    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
    [2.435781][2.435781:435854]()
    txn.touch_channel(&mut *channel_.write(), None);
    [2.435781]
    [2.435854]
    txn_.touch_channel(&mut *channel__, None);
    std::mem::drop(channel__);
    std::mem::drop(txn_);
  • edit in api/src/replication.rs at line 149
    [2.436275]
    [2.436275]
    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
    [2.436848][2.436848:436940]()
    let result = txn.unrecord(&repo_.changes, &mut channel_, &hash, 0);
    [2.436848]
    [2.436940]
    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
    [5.1409][5.1409:1430]()
    t: Option<&str>,
    [5.1409]
    [5.1430]
    _t: Option<&str>,
  • edit in api/src/permissions.rs at line 18
    [2.446457]
    [2.446457]
    const READ_JOBS = 0x100;
    const WRITE_JOBS = 0x200;
  • replacement in api/src/permissions.rs at line 26
    [2.446515][2.446515:446560]()
    Perm::READ | Perm::CREATE_DISCUSSION
    [2.446515]
    [2.446560]
    Perm::READ | Perm::CREATE_DISCUSSION | Perm::READ_JOBS
  • replacement in api/src/main.rs at line 2
    [2.452167][2.452167:452186]()
    debug_handler,
    [2.452167]
    [2.452186]
    Json, Router, debug_handler,
  • edit in api/src/main.rs at line 7
    [2.452339][2.452339:452357]()
    Json, Router,
  • edit in api/src/main.rs at line 31
    [2.452992]
    [2.452992]
    mod jobs;
  • replacement in api/src/main.rs at line 74
    [3.9430][5.7054:7159]()
    .allow_origin([
    config_file.origin.parse::<http::HeaderValue>().unwrap(),
    ])
    [3.9430]
    [3.9572]
    .allow_origin([config_file.origin.parse::<http::HeaderValue>().unwrap()])
  • replacement in api/src/main.rs at line 76
    [3.9606][5.7160:7263]()
    /*
    let cors = CorsLayer::new()
    .allow_origin(tower_http::cors::AllowOrigin::any());
    */
    [3.9606]
    [2.454258]
  • replacement in api/src/main.rs at line 80
    [2.454428][2.454428:454517]()
    let handler = crate::replication::H::new(repo_locks.clone(), config.db.clone());
    [2.454428]
    [2.454517]
    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
    [2.456037][5.7500:7549]()
    let headers = req.headers();
  • edit in api/src/main.rs at line 186
    [5.8550][5.8550:8601]()
    use axum::handler::HandlerWithoutStateExt;
  • replacement in api/src/main.rs at line 230
    [5.9157][5.9157:9258]()
    async fn logout(State(config): State<Config>, jar: SignedCookieJar) -> (SignedCookieJar, Redirect) {
    [5.9157]
    [5.9258]
    async fn logout(
    State(_config): State<Config>,
    jar: SignedCookieJar,
    ) -> (SignedCookieJar, Redirect) {
  • replacement in api/src/main.rs at line 353
    [2.461640][2.461640:461754]()
    let (id, login) = if let Some((id, login)) = get_user_login(&jar, &config).await? {
    (id, Some(login))
    [2.461640]
    [2.461754]
    let login = if let Some((_, login)) = get_user_login(&jar, &config).await? {
    Some(login)
  • replacement in api/src/main.rs at line 356
    [2.461767][2.461767:461801]()
    (uuid::Uuid::nil(), None)
    [2.461767]
    [2.461801]
    None
  • edit in api/src/main.rs at line 420
    [2.463655]
    [2.463655]
    .nest("/job", jobs::router())
  • replacement in api/src/main.rs at line 557
    [2.466955][2.466955:467054]()
    libpijul::pristine::sanakirja::GenericTxn<sanakirja::MutTxn<Arc<sanakirja::Env>, ()>>,
    [2.466955]
    [2.467054]
    libpijul::pristine::sanakirja::GenericTxn<sanakirja::MutTxn<Arc<sanakirja::Env>>>,
  • edit in api/src/main.rs at line 559
    [2.467082]
    [2.467082]
    std::io::Error,
  • replacement in api/src/main.rs at line 567
    [2.467227][2.467227:467326]()
    libpijul::pristine::sanakirja::GenericTxn<sanakirja::MutTxn<Arc<sanakirja::Env>, ()>>,
    [2.467227]
    [2.467326]
    libpijul::pristine::sanakirja::GenericTxn<sanakirja::MutTxn<Arc<sanakirja::Env>>>,
  • edit in api/src/main.rs at line 615
    [2.468858]
    [2.468858]
    #[error("Not found")]
    NotFound,
  • edit in api/src/main.rs at line 663
    [2.470716]
    [2.470716]
    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
    [2.487732][2.487732:487830]()
    use crate::{fallback, get_user_login, get_user_login_email, get_user_login_email_strict, Config};
    [2.487732]
    [2.487830]
    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
    [2.487842][2.487842:487861]()
    debug_handler,
    [2.487842]
    [2.487861]
    Form, Json, Router, debug_handler,
  • edit in api/src/discussions/mod.rs at line 10
    [2.487985][2.487985:488009]()
    Form, Json, Router,
  • edit in api/src/discussions/mod.rs at line 11
    [2.488012]
    [2.488012]
    use axum_extra::TypedHeader;
  • edit in api/src/discussions/mod.rs at line 13
    [2.488054][2.488054:488083]()
    use axum_extra::TypedHeader;
  • edit in api/src/discussions/mod.rs at line 18
    [2.488246]
    [2.488246]
    use diesel_async::AsyncPgConnection;
  • edit in api/src/discussions/mod.rs at line 20
    [2.488297][2.488297:488334]()
    use diesel_async::AsyncPgConnection;
  • edit in api/src/discussions/mod.rs at line 21
    [2.488384][2.488384:488417]()
    use crate::repository::TreePath;
  • edit in api/src/discussions/mod.rs at line 23
    [2.488440]
    [2.488440]
    use libpijul::Base32;
  • edit in api/src/discussions/mod.rs at line 25
    [2.488480][2.488480:488502]()
    use libpijul::Base32;
  • edit in api/src/db.rs at line 125
    [2.524315]
    [2.524315]
    }
    }
    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
    [2.528848]
    [2.528848]
    diesel::joinable!(jobs -> repositories (repo));
  • edit in api/src/db.rs at line 343
    [2.529594]
    [2.529594]
    jobs,
  • edit in api/src/config_file.rs at line 2
    [2.529843]
    [2.529843]
    use std::path::PathBuf;
  • edit in api/src/config_file.rs at line 8
    [5.9657]
    [2.529936]
    }
    #[derive(Default, Debug, Clone, Serialize, Deserialize)]
    pub struct CiConfig {
    pub filesystem: Option<PathBuf>,
  • edit in api/src/config_file.rs at line 57
    [2.531062][2.531062:531088]()
    pub ci: CiConfigFile,
  • edit in api/src/config_file.rs at line 67
    [2.531385]
    [2.531385]
    pub ci: CiConfig,
  • edit in api/src/config_file.rs at line 90
    [2.531901][2.531901:532183]()
    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
    [2.535282]
    [2.535282]
    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
    [2.535285]
    [2.535285]
    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
    [2.542456]
    [2.542456]
    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
    [2.555043][2.555043:555084]()
    date: chrono::DateTime<chrono::Utc>,
    [2.555043]
    [2.555084]
    date: jiff::Timestamp,
  • replacement in api/src/auth.rs at line 3
    [2.589771][2.589771:589790]()
    debug_handler,
    [2.589771]
    [2.589790]
    Form, debug_handler,
  • edit in api/src/auth.rs at line 6
    [2.589878][2.589878:589888]()
    Form,
  • replacement in api/src/auth.rs at line 10
    [2.589984][2.589984:590076]()
    sql_types::Bool, BoolExpressionMethods, ExpressionMethods, OptionalExtension, QueryDsl,
    [2.589984]
    [2.590076]
    BoolExpressionMethods, ExpressionMethods, OptionalExtension, QueryDsl, sql_types::Bool,
  • replacement in api/src/auth.rs at line 14
    [2.590151][2.590151:590173]()
    use rand::{rng, Rng};
    [2.590151]
    [2.590173]
    use rand::{Rng, rng};
  • replacement in api/src/auth.rs at line 271
    [2.597673][2.597673:597813]()
    #[error("This user is inactive. Please contact <a href=\"mailto:support@pijul.org\">support@pijul.org</a> to resolve this situation.")]
    [2.597673]
    [2.597813]
    #[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
    [3.13438][3.13438:13490]()
    axum = { version = "0.8.7", features = ["macros"] }
    [3.13438]
    [3.13490]
    axum = { version = "0.8.7", features = ["macros", "ws"] }
  • edit in api/Cargo.toml at line 39
    [3.14164]
    [2.603413]
    inotify = "0.11.1"
  • edit in api/Cargo.toml at line 41
    [2.603432]
    [2.603432]
    jiff = "0.2.16"
    jiff-diesel = { version = "0.1.3", features = ["postgres"] }
  • replacement in api/Cargo.toml at line 44
    [2.603454][2.603454:603481]()
    libpijul = "1.0.0-beta.10"
    [2.603454]
    [2.603481]
    libc = "0.2.185"
    libpijul = "1.0.0-beta.11"
  • replacement in api/Cargo.toml at line 62
    [2.603882][2.603882:603902]()
    sanakirja = "1.4.3"
    [2.603882]
    [3.14357]
    sanakirja = "2.0.0-beta"
  • edit in Cargo.nix at line 8071
    [6.243462]
    [6.243462]
    };
    "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
    [6.293671]
    [6.293671]
    name = "inotify";
    packageId = "inotify";
    }
    {
  • edit in Cargo.lock at line 320
    [2.614137]
    [2.614137]
    "base64 0.22.1",
  • edit in Cargo.lock at line 339
    [2.614444]
    [2.614444]
    "sha1",
  • edit in Cargo.lock at line 342
    [2.614471]
    [3.16904]
    "tokio-tungstenite",
  • edit in Cargo.lock at line 595
    [3.18761]
    [2.620858]
    dependencies = [
    "serde_core",
    ]
  • edit in Cargo.lock at line 1236
    [2.635390]
    [2.635390]
    "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
    [2.635438]
    [2.635438]
    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
    [2.643057][2.643057:643078]()
    "curve25519-dalek",
    [2.643057]
    [2.643078]
    "curve25519-dalek 3.2.0",
  • edit in Cargo.lock at line 1711
    [2.644912]
    [2.644912]
    [[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
    [3.26991][2.646304:646335](),[2.646304][2.646304:646335]()
    name = "fs2"
    version = "0.4.3"
    [3.26991]
    [2.646335]
    name = "fs4"
    version = "0.6.6"
  • replacement in Cargo.lock at line 1796
    [2.646400][2.646400:646478]()
    checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
    [2.646400]
    [2.646478]
    checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47"
  • replacement in Cargo.lock at line 1798
    [2.646495][2.646495:646515]()
    "libc",
    "winapi",
    [2.646495]
    [2.646515]
    "rustix 0.38.44",
    "windows-sys 0.48.0",
  • edit in Cargo.lock at line 2535
    [2.664907]
    [2.664907]
    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
    [3.31075]
    [2.665149]
    ]
    [[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
    [2.665164]
    [2.665164]
    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
    [3.32061]
    [3.32061]
    "jiff-tzdb-platform",
  • edit in Cargo.lock at line 2659
    [3.32129]
    [3.32129]
    "windows-sys 0.61.2",
  • edit in Cargo.lock at line 2663
    [3.32144]
    [3.32144]
    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
    [3.32322]
    [3.32322]
    ]
    [[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
    [2.667925][3.32745:32765]()
    version = "0.2.177"
    [2.667925]
    [2.667945]
    version = "0.2.185"
  • replacement in Cargo.lock at line 2740
    [2.668010][3.32766:32844]()
    checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
    [2.668010]
    [2.668332]
    checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
  • replacement in Cargo.lock at line 2750
    [2.668552][2.668552:668578]()
    version = "1.0.0-beta.10"
    [2.668552]
    [2.668578]
    version = "1.0.0-beta.11"
  • replacement in Cargo.lock at line 2752
    [2.668643][2.668643:668721]()
    checksum = "230807b6b791be169a67d951be0a4c3f1a231281cfc2a8caddec6804e188517a"
    [2.668643]
    [2.668721]
    checksum = "f625e90234ef1fd164dfe7980ffb04a3036a08e31e051cc29e6273a0c9a7a7e7"
  • replacement in Cargo.lock at line 2757
    [3.32962][2.668776:668795](),[2.668776][2.668776:668795]()
    "bitflags 1.3.2",
    [3.32962]
    [2.668795]
    "bitflags 2.10.0",
  • edit in Cargo.lock at line 2762
    [2.668854][2.668854:668865]()
    "cfg-if",
  • edit in Cargo.lock at line 2763
    [2.668879][2.668879:668890]()
    "chrono",
  • replacement in Cargo.lock at line 2764
    [2.668910][2.668910:668931]()
    "curve25519-dalek",
    [2.668910]
    [2.668931]
    "curve25519-dalek 4.1.3",
  • replacement in Cargo.lock at line 2774
    [2.669070][2.669070:669086]()
    "lazy_static",
    [2.669070]
    [2.669086]
    "imara-diff",
    "jiff",
  • edit in Cargo.lock at line 2778
    [2.669108][2.669108:669119]()
    "memchr",
  • replacement in Cargo.lock at line 2779
    [3.32999][2.669127:669150](),[2.669127][2.669127:669150]()
    "parking_lot 0.11.2",
    [3.32999]
    [2.669150]
    "parking_lot 0.12.5",
  • replacement in Cargo.lock at line 2783
    [2.669199][2.669199:669214]()
    "rand 0.8.5",
    [2.669199]
    [2.669214]
    "rand 0.9.2",
  • replacement in Cargo.lock at line 2792
    [2.669316][2.669316:669353]()
    "thiserror 1.0.69",
    "toml 0.5.11",
    [2.669316]
    [2.669353]
    "thiserror 2.0.17",
    "toml 0.8.23",
  • edit in Cargo.lock at line 2869
    [2.670965]
    [3.33365]
    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
    [2.678307]
    [3.35422]
    "inotify",
  • edit in Cargo.lock at line 3165
    [3.35436]
    [2.678328]
    "jiff",
    "jiff-diesel",
  • edit in Cargo.lock at line 3168
    [2.678344]
    [2.678344]
    "libc",
  • replacement in Cargo.lock at line 3858
    [2.686318][2.686318:686336]()
    version = "0.1.5"
    [2.686318]
    [2.686336]
    version = "0.2.1"
  • replacement in Cargo.lock at line 3860
    [2.686401][2.686401:686479]()
    checksum = "498a099351efa4becc6a19c72aa9270598e8fd274ca47052e37455241c88b696"
    [2.686401]
    [2.686479]
    checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42"
  • replacement in Cargo.lock at line 4048
    [2.690018][2.690018:690036]()
    version = "0.5.0"
    [2.690018]
    [2.690036]
    version = "1.0.0-beta.11"
  • replacement in Cargo.lock at line 4050
    [2.690101][2.690101:690179]()
    checksum = "c2b3743cd80b65d40b9f20b6575016f76d4221754866316ffbf7d7b0d58e7064"
    [2.690101]
    [2.690179]
    checksum = "a794a27eb67b7ae7f940c4b94e186a58a525f87d11de1c1a0e317c9b7aafb163"
  • replacement in Cargo.lock at line 4054
    [2.690222][2.690222:690248]()
    "regex",
    "syn 1.0.109",
    [2.690222]
    [2.690248]
    "syn 2.0.111",
  • edit in Cargo.lock at line 4742
    [2.706320]
    [3.50489]
    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
    [2.706538][2.706538:706556]()
    "linux-raw-sys",
    [2.706538]
    [3.50608]
    "linux-raw-sys 0.11.0",
  • replacement in Cargo.lock at line 4833
    [2.708241][2.708241:708259]()
    version = "1.4.3"
    [2.708241]
    [2.708259]
    version = "2.0.0-beta"
  • replacement in Cargo.lock at line 4835
    [2.708324][2.708324:708402]()
    checksum = "81aaf70d064e2122209f04d01fd91e8908e7a327b516236e1cbc0c3f34ac6d11"
    [2.708324]
    [2.708402]
    checksum = "4fc53a1e7a19d27c070749c0d2717427d1612c9b100274b205dbb438e605e34a"
  • replacement in Cargo.lock at line 4838
    [2.708433][2.708433:708441]()
    "fs2",
    [2.708433]
    [2.708441]
    "fs4",
  • edit in Cargo.lock at line 4840
    [2.708457]
    [2.708457]
    "libc",
  • replacement in Cargo.lock at line 4851
    [2.708589][2.708589:708607]()
    version = "1.4.1"
    [2.708589]
    [2.708607]
    version = "2.0.0-beta"
  • replacement in Cargo.lock at line 4853
    [2.708672][2.708672:708750]()
    checksum = "8376db34ae3eac6e7bd91168bc638450073b708ce9fb46940de676f552238bf5"
    [2.708672]
    [2.708750]
    checksum = "48a8500bcb79fe605b1433c30e8c5c078810b24dc4d04ffda35e88d712ff16f0"
  • edit in Cargo.lock at line 5019
    [3.53051]
    [2.712102]
    ]
    [[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
    [2.720207][2.720207:720218]()
    "rustix",
    [2.720207]
    [3.55445]
    "rustix 1.1.2",
  • edit in Cargo.lock at line 5676
    [2.726780]
    [2.726780]
    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
    [2.727083][2.727083:727102]()
    version = "0.5.11"
    [2.727083]
    [2.727102]
    version = "0.8.23"
  • replacement in Cargo.lock at line 5704
    [2.727167][2.727167:727245]()
    checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
    [2.727167]
    [2.727245]
    checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
  • edit in Cargo.lock at line 5706
    [2.727262]
    [2.727262]
    "indexmap",
  • edit in Cargo.lock at line 5708
    [2.727272]
    [2.727272]
    "serde_spanned 0.6.9",
    "toml_datetime 0.6.11",
    "toml_edit",
  • replacement in Cargo.lock at line 5721
    [3.57691][2.727490:727526](),[2.727490][2.727490:727526]()
    "serde_spanned",
    "toml_datetime",
    [3.57691]
    [3.57692]
    "serde_spanned 1.0.3",
    "toml_datetime 0.7.3",
  • edit in Cargo.lock at line 5730
    [2.727578]
    [3.57736]
    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
    [2.727781]
    [3.57850]
    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
    [2.728052]
    [2.728052]
    [[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
    [2.731359]
    [2.731359]
    [[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
    [3.63445]
    [2.743483]
    ]
    [[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
    [2.743993]
    [2.743993]
    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
    [2.744369]
    [2.744369]
    [[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
    [2.744620]
    [2.744620]
    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
    [2.744821]
    [2.744821]
    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
    [3.65394]
    [2.745188]
    [[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
    [2.745431]
    [2.745431]
    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
    [3.65788]
    [3.65788]
    [[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
    [3.65996]
    [3.65996]
    [[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
    [3.66252]
    [2.746461]
    dependencies = [
    "memchr",
    ]
  • replacement in Cargo.lock at line 6846
    [2.747644][2.747644:747655]()
    "rustix",
    [2.747626]
    [2.747655]
    "rustix 1.1.2",