1234module cli56import json27import os8import src.build9import src.exchange10import src.fmt11import src.history12import src.machine13import src.release14import src.remote15import src.storage1617pub fn cmd_offer(raw []string) int {18 args := parse('offer', raw, Options{19 values: ['notes']20 flags: ['json', 'builds']21 }) or { return usage_error(err.msg()) }22 if args.value('notes') != '' && !args.flag('builds') {23 return usage_error('--notes belongs to --builds')24 }25 store := open_store() or { return refuse(args.json(), err.msg()) }26 defer {27 store.close()28 }29 mut backend := this_machine_backend() or { return refuse(args.json(), err.msg()) }30 if args.flag('builds') {31 return offer_builds(args, store, mut backend)32 }33 report := exchange.offer(store, mut backend) or { return refuse(args.json(), err.msg()) }34 if args.json() {35 emit_json(json2.encode(OfferedJson{36 repo: report.repo37 saves: report.saves38 objects: report.objects39 events: report.events40 head: report.head41 }))42 return 043 }44 if report.saves + report.objects + report.events == 0 {45 ok('the remote already has everything from [${report.head}]')46 return 047 }48 ok('offered ${report.saves} saves · ${report.objects} objects · ${report.events} events to "${report.repo}"')49 note('remote head is now [${report.head}]')50 return 051}5253545556fn offer_builds(args Args, store &storage.Store, mut backend remote.Backend) int {57 head := history.head(store) or { return refuse(args.json(), exchange.no_saves) }58 if exchange.dirty(store, head) or { true } {59 return refuse(args.json(), 'save or checkpoint first — a release is built from a save')60 }61 report := exchange.offer(store, mut backend) or { return refuse(args.json(), err.msg()) }62 config := build.read_config(store.root) or { return refuse(args.json(), err.msg()) }63 64 release_version := build.project_version(store.root) or {65 return refuse(args.json(), err.msg())66 }67 notes := args.value('notes')68 release.check_notes(notes) or { return refuse(args.json(), err.msg()) }69 published := release.publish(store, mut backend, report.repo, config, release_version, notes) or {70 return refuse(args.json(), err.msg())71 }72 if args.json() {73 emit_json(json2.encode(PublishedJson{74 repo: report.repo75 version: published.version76 files: published.files.map(PublishedFileJson{77 platform: it.platform78 name: it.name79 size: it.size80 blake3: it.blake381 })82 }))83 return 084 }85 for file in published.files {86 ok('${file.platform} ${file.name} (${fmt.human_bytes(file.size)})')87 }88 ok('published ${published.version} of "${report.repo}" from [${report.head}]')89 return 090}919293pub fn this_machine_backend() !remote.Backend {94 settings := machine.load_remote()!95 if settings.kind == machine.kind_folder {96 97 98 99 if !os.is_dir(settings.path) {100 return error('the remote folder ${settings.path} is not there — is its disk mounted? ("koh remote" changes it)')101 }102 return remote.new_folder(settings.path)!103 }104 credentials := machine.load_credentials()!105 return remote.new_s3(settings.endpoint, settings.bucket, settings.region,106 credentials.access_key, credentials.secret_key)!107}108109110111112pub fn auto_offer(store &storage.Store, quiet bool) {113 if !machine.has_remote() {114 return115 }116 mut backend := this_machine_backend() or {117 if !quiet {118 note('not offered: ${err.msg()}')119 }120 return121 }122 report := exchange.offer(store, mut backend) or {123 if !quiet {124 note('not offered: ${err.msg()}')125 }126 return127 }128 if !quiet && report.saves + report.objects + report.events > 0 {129 note('offered to "${report.repo}" — ${report.saves} saves · ${report.objects} objects')130 }131}132133struct OfferedJson {134 repo string135 saves int136 objects int137 events int138 head string139}140141struct PublishedFileJson {142 platform string143 name string144 size i64145 blake3 string146}147148struct PublishedJson {149 repo string150 version string151 files []PublishedFileJson152}