OXYGENHUB
# 16ee5d19d220 oxygenhub / crates / hub / src / web / artifacts.rs
3.9 KiB · Rust · last changed Repository bar: an Artifacts menu and a More menu 18 days ago source blame history raw
1
//! A repository's Artifacts menu (oxygenhub#254): every run's build
2
//! artifacts in one list, the runner images its jobs use, and its
3
//! Terraform state. Releases and reports keep their own pages.
4
5
use super::{filters, lookup, Error, Page, RepoShell, Shell};
6
use crate::auth::Viewer;
7
use crate::db::flow as fdb;
8
use crate::{db, Shared};
9
use askama::Template;
10
use axum::extract::{Path, State};
11
12
/// At most this many artifacts are listed, newest first.
13
const ARTIFACTS_SHOWN: i64 = 200;
14
/// Images used within this many days are listed.
15
const IMAGE_DAYS: i64 = 90;
16
17
#[derive(Template)]
18
#[template(path = "repo_artifacts.html")]
19
pub struct ArtifactsPage {
20
    shell: Shell,
21
    repo: RepoShell,
22
    artifacts: Vec<fdb::RepoArtifact>,
23
    limit: i64,
24
}
25
26
/// `GET /{owner}/{name}/artifacts`.
27
pub async fn artifacts(
28
    State(app): State<Shared>,
29
    viewer: Viewer,
30
    Path((owner, name)): Path<(String, String)>,
31
) -> Result<Page<ArtifactsPage>, Error> {
32
    let shell = Shell::new(&app, &format!("/{owner}/{name}/artifacts"), &viewer);
33
    let (_repo, row, role) = lookup(&app, &shell, &viewer, &owner, &name).await?;
34
    let artifacts = fdb::repo_artifacts(&app.db, row.id, ARTIFACTS_SHOWN).await.map_err(|e| Error::from_db(shell.clone(), e))?;
35
    Ok(Page(ArtifactsPage { shell, repo: RepoShell::new(&app, &row, "artifacts").with_role(role), artifacts, limit: ARTIFACTS_SHOWN }))
36
}
37
38
/// One image this repository's jobs ran on.
39
pub struct ImageView {
40
    /// The image's BLAKE3, or what the pipeline named when it is not a hash.
41
    pub image: String,
42
    pub hashed: bool,
43
    pub jobs: i64,
44
    pub last_ms: i64,
45
    /// Kept by the hub: its size and architecture.
46
    pub kept: Option<(i64, String)>,
47
}
48
49
#[derive(Template)]
50
#[template(path = "repo_images.html")]
51
pub struct ImagesPage {
52
    shell: Shell,
53
    repo: RepoShell,
54
    images: Vec<ImageView>,
55
    days: i64,
56
}
57
58
/// `GET /{owner}/{name}/images`.
59
pub async fn images(
60
    State(app): State<Shared>,
61
    viewer: Viewer,
62
    Path((owner, name)): Path<(String, String)>,
63
) -> Result<Page<ImagesPage>, Error> {
64
    let shell = Shell::new(&app, &format!("/{owner}/{name}/images"), &viewer);
65
    let (_repo, row, role) = lookup(&app, &shell, &viewer, &owner, &name).await?;
66
    let e = |e| Error::from_db(shell.clone(), e);
67
    let uses = crate::images::uses_of(&app.db, row.id, db::now_ms() - IMAGE_DAYS * 24 * 3600 * 1000).await.map_err(e)?;
68
    let kept = crate::images::kept(&app.db).await.map_err(e)?;
69
    let images = uses
70
        .into_iter()
71
        .map(|u| {
72
            let kept = kept.iter().find(|k| k.hash.eq_ignore_ascii_case(&u.image)).map(|k| (k.size, k.arch.clone()));
73
            ImageView { hashed: jet::images::is_hash(&u.image), image: u.image, jobs: u.jobs, last_ms: u.last_ms, kept }
74
        })
75
        .collect();
76
    Ok(Page(ImagesPage { shell, repo: RepoShell::new(&app, &row, "images").with_role(role), images, days: IMAGE_DAYS }))
77
}
78
79
#[derive(Template)]
80
#[template(path = "repo_terraform.html")]
81
pub struct TerraformPage {
82
    shell: Shell,
83
    repo: RepoShell,
84
    tf_states: Vec<(db::terraform::TfState, i64)>,
85
}
86
87
/// `GET /{owner}/{name}/terraform`: the states the hub keeps for this
88
/// repository (oxygenhub#252), and the backend block that uses them.
89
pub async fn terraform(
90
    State(app): State<Shared>,
91
    viewer: Viewer,
92
    Path((owner, name)): Path<(String, String)>,
93
) -> Result<Page<TerraformPage>, Error> {
94
    let shell = Shell::new(&app, &format!("/{owner}/{name}/terraform"), &viewer);
95
    let (_repo, row, role) = lookup(&app, &shell, &viewer, &owner, &name).await?;
96
    let e = |e| Error::from_db(shell.clone(), e);
97
    let mut tf_states = Vec::new();
98
    for s in db::terraform::of_repo(&app.db, row.id).await.map_err(e)? {
99
        let n = db::terraform::versions(&app.db, row.id, &s.name).await.map_err(e)?;
100
        tf_states.push((s, n));
101
    }
102
    Ok(Page(TerraformPage { shell, repo: RepoShell::new(&app, &row, "terraform").with_role(role), tf_states }))
103
}