lol
This commit is contained in:
parent
0cc3e123b2
commit
62848ea8c8
8 changed files with 2960 additions and 35 deletions
30
.github/workflows/build.yml
vendored
30
.github/workflows/build.yml
vendored
|
@ -1,30 +0,0 @@
|
|||
name: Build Agda
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest]
|
||||
# agda-version: [2.6.2, 2.7.0]
|
||||
|
||||
steps:
|
||||
- name: Cache cabal
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cabal
|
||||
key: cabal-dir-${{ matrix.os }}
|
||||
- uses: haskell-actions/setup@v2
|
||||
with:
|
||||
ghc-version: 9.8.2
|
||||
cabal-version: 3.10.3.0
|
||||
- run: mkdir -p installdir
|
||||
- name: Install Agda
|
||||
run: cabal install --installdir installdir Agda
|
||||
- run: tar cvf agda.tar.gz -C "$(dirname $(dirname $(realpath installdir/agda)))" .
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: agda.tar.gz
|
||||
path: agda.tar.gz
|
2699
Cargo.lock
generated
Normal file
2699
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "agdaup"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.92"
|
||||
clap = { version = "4.5.20", features = ["derive"] }
|
||||
dirs = "5.0.1"
|
||||
futures = "0.3.31"
|
||||
manic = "0.8.1"
|
||||
phf = { version = "0.11.2", features = ["macros"] }
|
||||
reqwest = { version = "0.12.9", features = ["json", "stream"] }
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_json = "1.0.132"
|
||||
tokio = { version = "1.41.0", features = ["macros", "rt-multi-thread"] }
|
||||
zip-extract = { version = "0.2.1", features = ["bzip2", "chrono", "deflate", "deflate-flate2", "deflate-miniz", "deflate-zlib"] }
|
|
@ -1,2 +0,0 @@
|
|||
FROM ubuntu:latest
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
services:
|
||||
dev:
|
||||
build: .
|
2
installers/install.sh
Normal file
2
installers/install.sh
Normal file
|
@ -0,0 +1,2 @@
|
|||
#!/usr/bin/env bash
|
||||
curl
|
109
src/get_latest_release_info.rs
Normal file
109
src/get_latest_release_info.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
use std::env::consts::{ARCH, OS};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use phf::phf_map;
|
||||
use reqwest::{
|
||||
header::{HeaderMap, HeaderValue},
|
||||
Client, ClientBuilder,
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Release {
|
||||
pub id: u64,
|
||||
pub tag_name: String,
|
||||
pub assets: Vec<Asset>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Asset {
|
||||
pub id: u64,
|
||||
pub url: String,
|
||||
pub size: u64,
|
||||
pub name: String,
|
||||
pub browser_download_url: String,
|
||||
}
|
||||
|
||||
impl Asset {
|
||||
pub fn get_asset_info(&self) -> AssetInfo {
|
||||
let parts = self.name.split("-").collect::<Vec<_>>();
|
||||
assert!(parts[0] == "agda");
|
||||
let version = parts[1].to_owned();
|
||||
let arch = parts[2].to_owned();
|
||||
let os = parts
|
||||
.iter()
|
||||
.skip(3)
|
||||
.take_while(|p| !p.starts_with("ghc"))
|
||||
.map(|s| *s)
|
||||
.collect::<Vec<_>>()
|
||||
.join("-");
|
||||
let ghc = parts[3 + os.split("-").count()]
|
||||
.trim_start_matches("ghc")
|
||||
.to_owned();
|
||||
AssetInfo {
|
||||
arch,
|
||||
version,
|
||||
os,
|
||||
ghc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AssetInfo {
|
||||
pub arch: String,
|
||||
pub version: String,
|
||||
pub os: String,
|
||||
pub ghc: String,
|
||||
}
|
||||
|
||||
static ALLOWED_ARCHS: phf::Map<&'static str, &'static [&'static str]> = phf_map! {
|
||||
"x86_64" => &["x64"],
|
||||
"aarch64" => &["arm64"],
|
||||
};
|
||||
|
||||
static ALLOWED_OSS: phf::Map<&'static str, &'static [&'static str]> = phf_map! {
|
||||
"macos" => &["macos"],
|
||||
"windows" => &["windows"],
|
||||
};
|
||||
|
||||
impl AssetInfo {
|
||||
pub fn applies_to_this_machine(&self) -> bool {
|
||||
let allowed_archs = match ALLOWED_ARCHS.get(&ARCH) {
|
||||
Some(v) => v,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if !allowed_archs.contains(&self.arch.as_str()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let parts = self.os.split("-").collect::<Vec<_>>();
|
||||
|
||||
let allowed_oss = match ALLOWED_OSS.get(&OS) {
|
||||
Some(v) => v,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if !allowed_oss.contains(&parts[0]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Any more?
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_latest_github_release_info(client: &Client) -> Result<Release> {
|
||||
let resp = client
|
||||
.get("https://api.github.com/repos/wenkokke/setup-agda/releases")
|
||||
.send()
|
||||
.await?;
|
||||
let data: Vec<Release> = resp.json().await?;
|
||||
|
||||
let latest_release = match data.into_iter().find(|r| r.tag_name == "latest") {
|
||||
Some(v) => v,
|
||||
None => bail!("could not find latest release"),
|
||||
};
|
||||
|
||||
Ok(latest_release)
|
||||
}
|
133
src/main.rs
Normal file
133
src/main.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
mod get_latest_release_info;
|
||||
|
||||
use std::{
|
||||
fs::{self, File, Permissions},
|
||||
io::Write,
|
||||
os::unix::fs::PermissionsExt,
|
||||
};
|
||||
|
||||
use anyhow::{bail, Context, Result};
|
||||
use clap::{Parser, Subcommand};
|
||||
use futures::StreamExt;
|
||||
use get_latest_release_info::{Asset, AssetInfo};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Opt {
|
||||
#[clap(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Command {
|
||||
Show,
|
||||
Update,
|
||||
Install,
|
||||
List {
|
||||
/// List available versions
|
||||
#[clap(short, long)]
|
||||
available: bool,
|
||||
},
|
||||
}
|
||||
|
||||
// Rough layout
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let opt = Opt::parse();
|
||||
println!("Hello, world!");
|
||||
|
||||
let client = {
|
||||
use reqwest::{
|
||||
header::{HeaderMap, HeaderValue},
|
||||
ClientBuilder,
|
||||
};
|
||||
let mut h = HeaderMap::new();
|
||||
h.insert("User-Agent", HeaderValue::from_str("mzhang28-agdaup")?);
|
||||
ClientBuilder::new().default_headers(h).build()?
|
||||
};
|
||||
|
||||
let bin_dir = dirs::executable_dir();
|
||||
|
||||
let data_dir = match dirs::data_dir() {
|
||||
Some(v) => v.join("agdaup"),
|
||||
None => bail!("no data dir?"),
|
||||
};
|
||||
|
||||
let my_bin_dir = data_dir.join("bin");
|
||||
|
||||
match opt.command {
|
||||
Command::Show => todo!(),
|
||||
Command::Update => todo!(),
|
||||
Command::Install => {
|
||||
let release_info =
|
||||
get_latest_release_info::get_latest_github_release_info(&client).await?;
|
||||
|
||||
let mut assets = release_info
|
||||
.assets
|
||||
.iter()
|
||||
.filter(|asset| asset.get_asset_info().applies_to_this_machine())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assets.sort_by_key(|asset| &asset.name);
|
||||
|
||||
let desired_asset = assets[assets.len() - 1];
|
||||
let asset_info = desired_asset.get_asset_info();
|
||||
|
||||
let version_dir = data_dir.join("versions").join(asset_info.version);
|
||||
fs::remove_dir_all(&version_dir).context("could not remove version dir")?;
|
||||
fs::create_dir_all(&version_dir).context("could not create version dir")?;
|
||||
|
||||
println!("Downloading {}", desired_asset.browser_download_url);
|
||||
|
||||
let resp = client
|
||||
.get(&desired_asset.browser_download_url)
|
||||
.send()
|
||||
.await
|
||||
.context("could not get url")?;
|
||||
|
||||
let download_path = version_dir.join("download.zip");
|
||||
|
||||
let mut stream = resp.bytes_stream();
|
||||
|
||||
{
|
||||
let mut file = File::create(&download_path)?;
|
||||
while let Some(chunk) = stream.next().await {
|
||||
let chunk = chunk?;
|
||||
file.write_all(&chunk)?;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let file = File::open(&download_path).context("could not open downloaded file")?;
|
||||
zip_extract::extract(&file, &version_dir, true).context("could not extract")?;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let agda_bin = version_dir.join("bin").join("agda");
|
||||
let meta = fs::metadata(&agda_bin).context("could not get metadata")?;
|
||||
let mut perm = meta.permissions();
|
||||
perm.set_mode(perm.mode() | 0o100);
|
||||
fs::set_permissions(&agda_bin, perm).context("could not set permission")?;
|
||||
|
||||
fs::create_dir_all(&my_bin_dir).context("could not create bin dir")?;
|
||||
fs::remove_file(my_bin_dir.join("agda"))
|
||||
.context("could not remove existing symlink")?;
|
||||
std::os::unix::fs::symlink(&agda_bin, my_bin_dir.join("agda"))
|
||||
.context("could not symlink")?;
|
||||
|
||||
if let Some(bin_dir) = bin_dir {
|
||||
fs::remove_file(bin_dir.join("agda"));
|
||||
std::os::unix::fs::symlink(&agda_bin, bin_dir.join("agda"))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Command::List { available } => todo!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue