mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Merge pull request #1363 from elly/cargo
cargo: support optional signing of packages.json files.
This commit is contained in:
commit
14883871c9
@ -14,3 +14,5 @@
|
||||
vers = "0.1",
|
||||
uuid = "9ff87a04-8fed-4295-9ff8-f99bb802650b",
|
||||
url = "http://rust-lang.org/doc/cargo")];
|
||||
|
||||
mod pgp;
|
||||
|
@ -30,16 +30,21 @@ type package = {
|
||||
name: str,
|
||||
uuid: str,
|
||||
url: str,
|
||||
method: str
|
||||
method: str,
|
||||
tags: [str]
|
||||
};
|
||||
|
||||
type source = {
|
||||
name: str,
|
||||
url: str,
|
||||
sig: option::t<str>,
|
||||
key: option::t<str>,
|
||||
keyfp: option::t<str>,
|
||||
mutable packages: [package]
|
||||
};
|
||||
|
||||
type cargo = {
|
||||
pgp: bool,
|
||||
root: str,
|
||||
bindir: str,
|
||||
libdir: str,
|
||||
@ -158,12 +163,32 @@ fn need_dir(s: str) {
|
||||
fn parse_source(name: str, j: json::json) -> source {
|
||||
alt j {
|
||||
json::dict(_j) {
|
||||
alt _j.find("url") {
|
||||
let url = alt _j.find("url") {
|
||||
some(json::string(u)) {
|
||||
ret { name: name, url: u, mutable packages: [] };
|
||||
u
|
||||
}
|
||||
_ { fail "Needed 'url' field in source."; }
|
||||
};
|
||||
let sig = alt _j.find("sig") {
|
||||
some(json::string(u)) {
|
||||
some(u)
|
||||
}
|
||||
_ { none }
|
||||
};
|
||||
let key = alt _j.find("key") {
|
||||
some(json::string(u)) {
|
||||
some(u)
|
||||
}
|
||||
_ { none }
|
||||
};
|
||||
let keyfp = alt _j.find("keyfp") {
|
||||
some(json::string(u)) {
|
||||
some(u)
|
||||
}
|
||||
_ { none }
|
||||
};
|
||||
ret { name: name, url: url, sig: sig, key: key, keyfp: keyfp,
|
||||
mutable packages: [] };
|
||||
}
|
||||
_ { fail "Needed dict value in source."; }
|
||||
};
|
||||
@ -217,18 +242,31 @@ fn load_one_source_package(&src: source, p: map::hashmap<str, json::json>) {
|
||||
}
|
||||
};
|
||||
|
||||
let tags = [];
|
||||
alt p.find("tags") {
|
||||
some(json::list(js)) {
|
||||
for j in *js {
|
||||
alt j {
|
||||
json::string(_j) { vec::grow(tags, 1u, _j); }
|
||||
_ { }
|
||||
}
|
||||
}
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
vec::grow(src.packages, 1u, {
|
||||
// source: _source(src),
|
||||
name: name,
|
||||
uuid: uuid,
|
||||
url: url,
|
||||
method: method
|
||||
method: method,
|
||||
tags: tags
|
||||
});
|
||||
info(" Loaded package: " + src.name + "/" + name);
|
||||
log " Loaded package: " + src.name + "/" + name;
|
||||
}
|
||||
|
||||
fn load_source_packages(&c: cargo, &src: source) {
|
||||
info("Loading source: " + src.name);
|
||||
log "Loading source: " + src.name;
|
||||
let dir = fs::connect(c.sourcedir, src.name);
|
||||
let pkgfile = fs::connect(dir, "packages.json");
|
||||
if !fs::path_exists(pkgfile) { ret; }
|
||||
@ -269,6 +307,7 @@ fn configure() -> cargo {
|
||||
try_parse_sources(fs::connect(p, "sources.json"), sources);
|
||||
try_parse_sources(fs::connect(p, "local-sources.json"), sources);
|
||||
let c = {
|
||||
pgp: pgp::supported(),
|
||||
root: p,
|
||||
bindir: fs::connect(p, "bin"),
|
||||
libdir: fs::connect(p, "lib"),
|
||||
@ -289,6 +328,10 @@ fn configure() -> cargo {
|
||||
sources.insert(k, s);
|
||||
};
|
||||
|
||||
if c.pgp {
|
||||
pgp::init(c.root);
|
||||
}
|
||||
|
||||
c
|
||||
}
|
||||
|
||||
@ -501,7 +544,10 @@ fn cmd_install(c: cargo, argv: [str]) {
|
||||
|
||||
fn sync_one(c: cargo, name: str, src: source) {
|
||||
let dir = fs::connect(c.sourcedir, name);
|
||||
let pkgfile = fs::connect(dir, "packages.json");
|
||||
let pkgfile = fs::connect(dir, "packages.json.new");
|
||||
let destpkgfile = fs::connect(dir, "packages.json");
|
||||
let sigfile = fs::connect(dir, "packages.json.sig");
|
||||
let keyfile = fs::connect(dir, "key.gpg");
|
||||
let url = src.url;
|
||||
need_dir(dir);
|
||||
info(#fmt["fetching source %s...", name]);
|
||||
@ -511,6 +557,43 @@ fn sync_one(c: cargo, name: str, src: source) {
|
||||
} else {
|
||||
info(#fmt["fetched source: %s", name]);
|
||||
}
|
||||
alt src.sig {
|
||||
some(u) {
|
||||
let p = run::program_output("curl", ["-f", "-s", "-o", sigfile,
|
||||
u]);
|
||||
if p.status != 0 {
|
||||
warn(#fmt["fetch for source %s (sig %s) failed", name, u]);
|
||||
}
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
alt src.key {
|
||||
some(u) {
|
||||
let p = run::program_output("curl", ["-f", "-s", "-o", keyfile,
|
||||
u]);
|
||||
if p.status != 0 {
|
||||
warn(#fmt["fetch for source %s (key %s) failed", name, u]);
|
||||
}
|
||||
pgp::add(c.root, keyfile);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
alt (src.sig, src.key, src.keyfp) {
|
||||
(some(_), some(_), some(f)) {
|
||||
let r = pgp::verify(c.root, pkgfile, sigfile, f);
|
||||
if !r {
|
||||
warn(#fmt["signature verification failed for source %s",
|
||||
name]);
|
||||
ret;
|
||||
} else {
|
||||
info(#fmt["signature ok for source %s", name]);
|
||||
}
|
||||
}
|
||||
_ {
|
||||
info(#fmt["no signature for source %s", name]);
|
||||
}
|
||||
}
|
||||
run::run_program("cp", [pkgfile, destpkgfile]);
|
||||
}
|
||||
|
||||
fn cmd_sync(c: cargo, argv: [str]) {
|
||||
@ -523,10 +606,75 @@ fn cmd_sync(c: cargo, argv: [str]) {
|
||||
}
|
||||
}
|
||||
|
||||
fn cmd_init(c: cargo) {
|
||||
let srcurl = "http://www.rust-lang.org/cargo/sources.json";
|
||||
let sigurl = "http://www.rust-lang.org/cargo/sources.json.sig";
|
||||
|
||||
let srcfile = fs::connect(c.root, "sources.json.new");
|
||||
let sigfile = fs::connect(c.root, "sources.json.sig");
|
||||
let destsrcfile = fs::connect(c.root, "sources.json");
|
||||
|
||||
let p = run::program_output("curl", ["-f", "-s", "-o", srcfile, srcurl]);
|
||||
if p.status != 0 {
|
||||
warn(#fmt["fetch of sources.json failed: %s", p.out]);
|
||||
ret;
|
||||
}
|
||||
|
||||
let p = run::program_output("curl", ["-f", "-s", "-o", sigfile, sigurl]);
|
||||
if p.status != 0 {
|
||||
warn(#fmt["fetch of sources.json.sig failed: %s", p.out]);
|
||||
ret;
|
||||
}
|
||||
|
||||
let r = pgp::verify(c.root, srcfile, sigfile, pgp::signing_key_fp());
|
||||
if !r {
|
||||
warn(#fmt["signature verification failed for sources.json"]);
|
||||
ret;
|
||||
}
|
||||
info(#fmt["signature ok for sources.json"]);
|
||||
run::run_program("cp", [srcfile, destsrcfile]);
|
||||
}
|
||||
|
||||
fn print_pkg(s: source, p: package) {
|
||||
let m = s.name + "/" + p.name + " (" + p.uuid + ")";
|
||||
if vec::len(p.tags) > 0u {
|
||||
m = m + " [" + str::connect(p.tags, ", ") + "]";
|
||||
}
|
||||
info(m);
|
||||
}
|
||||
fn cmd_list(c: cargo, argv: [str]) {
|
||||
for_each_package(c, { |s, p|
|
||||
if vec::len(argv) <= 2u || argv[2] == s.name {
|
||||
print_pkg(s, p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn cmd_search(c: cargo, argv: [str]) {
|
||||
if vec::len(argv) < 3u {
|
||||
cmd_usage();
|
||||
ret;
|
||||
}
|
||||
let n = 0;
|
||||
let name = argv[2];
|
||||
let tags = vec::slice(argv, 3u, vec::len(argv));
|
||||
for_each_package(c, { |s, p|
|
||||
if (str::contains(p.name, name) || name == "*") &&
|
||||
vec::all(tags, { |t| vec::member(t, p.tags) }) {
|
||||
print_pkg(s, p);
|
||||
n += 1;
|
||||
}
|
||||
});
|
||||
info(#fmt["Found %d packages.", n]);
|
||||
}
|
||||
|
||||
fn cmd_usage() {
|
||||
print("Usage: cargo <verb> [args...]");
|
||||
print(" init Fetch default sources");
|
||||
print(" install [source/]package-name Install by name");
|
||||
print(" install uuid:[source/]package-uuid Install by uuid");
|
||||
print(" list [source] List packages");
|
||||
print(" search <name | '*'> [tags...] Search packages");
|
||||
print(" sync Sync all sources");
|
||||
print(" usage This");
|
||||
}
|
||||
@ -538,7 +686,10 @@ fn main(argv: [str]) {
|
||||
}
|
||||
let c = configure();
|
||||
alt argv[1] {
|
||||
"init" { cmd_init(c); }
|
||||
"install" { cmd_install(c, argv); }
|
||||
"list" { cmd_list(c, argv); }
|
||||
"search" { cmd_search(c, argv); }
|
||||
"sync" { cmd_sync(c, argv); }
|
||||
"usage" { cmd_usage(); }
|
||||
_ { cmd_usage(); }
|
||||
|
103
src/cargo/pgp.rs
Normal file
103
src/cargo/pgp.rs
Normal file
@ -0,0 +1,103 @@
|
||||
use std;
|
||||
|
||||
import std::fs;
|
||||
import std::run;
|
||||
|
||||
fn gpg(args: [str]) -> { status: int, out: str, err: str } {
|
||||
ret run::program_output("gpg", args);
|
||||
}
|
||||
|
||||
fn signing_key() -> str {
|
||||
"
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: SKS 1.1.0
|
||||
|
||||
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
|
||||
=2z6P
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
"
|
||||
}
|
||||
|
||||
fn signing_key_fp() -> str {
|
||||
"FE79 EDB0 3DEF B0D8 27D2 6C41 0B2D 6A28 3033 6376"
|
||||
}
|
||||
|
||||
fn supported() -> bool {
|
||||
let r = gpg(["--version"]);
|
||||
r.status == 0
|
||||
}
|
||||
|
||||
fn init(root: str) {
|
||||
let p = fs::connect(root, "gpg");
|
||||
if !fs::path_is_dir(p) {
|
||||
fs::make_dir(p, 0x1c0i32);
|
||||
let p = run::start_program("gpg", ["--homedir", p, "--import"]);
|
||||
p.input().write_str(signing_key());
|
||||
let s = p.finish();
|
||||
if s != 0 {
|
||||
fail "pgp init failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add(root: str, key: str) {
|
||||
let path = fs::connect(root, "gpg");
|
||||
let p = run::program_output("gpg", ["--homedir", path, "--import", key]);
|
||||
if p.status != 0 {
|
||||
fail "pgp add failed: " + p.out;
|
||||
}
|
||||
}
|
||||
|
||||
fn verify(root: str, data: str, sig: str, keyfp: str) -> bool {
|
||||
let path = fs::connect(root, "gpg");
|
||||
let p = gpg(["--homedir", path, "--with-fingerprint", "--verify", sig,
|
||||
data]);
|
||||
let res = "Primary key fingerprint: " + keyfp;
|
||||
for line in str::split(p.err, '\n' as u8) {
|
||||
if line == res {
|
||||
ret true;
|
||||
}
|
||||
}
|
||||
ret false;
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
{
|
||||
"elly": {
|
||||
"url": "https://raw.github.com/elly/rust-packages/master/packages.json"
|
||||
"sig": "https://raw.github.com/elly/rust-packages/master/packages.json.sig",
|
||||
"key": "https://raw.github.com/elly/rust-packages/master/signing-key.gpg",
|
||||
"keyfp": "4107 21C0 FF32 858F 61FF 33F6 E595 8E36 FDC8 EA00"
|
||||
},
|
||||
"brson": {
|
||||
"url": "https://raw.github.com/brson/rust-packages/master/packages.json"
|
||||
|
Loading…
Reference in New Issue
Block a user