1 import Base 2 import ../release/type.bend as Rl 3 import ../release/ops.bend as RlO 4 import ./type.bend as P 5 6 def visibility(p: Bool) -> String: 7 match p: 8 case True{}: 9 "public" 10 case False{}: 11 "private" 12 13 # Bend hub rules apply to Bend packages, not to an application's whole repository. 14 def repo_steps(+fs: List<&2, String>, +reached: List<&2, String>, +package_reached: List<&2, String>, ps: List<&2, Rl.Package>) -> List<&2, P.Step>: 15 match ps: 16 case []: 17 [] 18 case _ <> _: 19 List.append(&2, P.Step, 20 Bool.pick(List<&2, P.Step>, String.is_empty(RlO.not_bend(package_reached)), [], 21 [P.Blocked{"package", RlO.not_bend(package_reached) ++ " is not Bend or a C/JavaScript effector"}]), 22 List.append(&2, P.Step, 23 Bool.pick(List<&2, P.Step>, Bool.not(RlO.bend_only(fs)) || String.is_empty(RlO.dead(fs, reached)), [], 24 [P.Blocked{"repository", RlO.dead(fs, reached) ++ " is dead code: no entry reaches it"}]), 25 Bool.pick(List<&2, P.Step>, String.is_empty(RlO.hub_refused(package_reached)), [], 26 [P.Blocked{"package", RlO.hub_refused(package_reached) ++ ": the Bend hub refuses this path (segments of letters, digits and _)"}]))) 27 28 def remote_step(+repo: Rl.Repository, rm: P.Remote) -> P.Step: 29 match repo rm: 30 case Rl.Repository{+name, +public} P.Remote{+origin, +vis, clean, pushed}: 31 Bool.pick(P.Step, String.contains(origin, name), 32 Bool.pick(P.Step, String.eq(vis, visibility(public)), 33 Bool.pick(P.Step, clean, 34 Bool.pick(P.Step, pushed, P.Current{"github " ++ name}, P.Push{name}), 35 P.Blocked{"github " ++ name, "uncommitted changes"}), 36 P.Blocked{"github " ++ name, "GitHub says " ++ Bool.pick(String, String.is_empty(vis), "nothing (unreachable)", vis) ++ ", declared " ++ visibility(public)}), 37 P.Blocked{"github " ++ name, "origin is " ++ origin}) 38 39 # ---- each package: already published as it is, new, or changed without a new version ---- 40 def package_step(p: Rl.Package, h: P.Hub) -> P.Step: 41 match p: 42 case Rl.Package{+n, +v, e, _}: 43 match h: 44 case P.Same{}: 45 P.Current{"package " ++ n ++ "@" ++ v} 46 case P.Unpublished{}: 47 P.Publish{n, v, e} 48 case P.Differs{f}: 49 P.Blocked{"package " ++ n ++ "@" ++ v, f ++ " changed since it was published; give it a new version"} 50 51 def package_steps(ps: List<&2, Rl.Package>, hs: List<&2, P.Hub>) -> List<&2, P.Step>: 52 match ps: 53 case []: 54 [] 55 case p <> prest: 56 match hs: 57 case []: 58 [] 59 case h <> hrest: 60 package_step(p, h) <> package_steps(prest, hrest) 61 62 # The plan: what a deploy would do, from the release and what was observed. 63 def plan(r: Rl.Release, o: P.Observed) -> List<&2, P.Step>: 64 match r o: 65 case Rl.Release{repo, +ps} P.Observed{+fs, +reached, +package_reached, hs, rm}: 66 List.append(&2, P.Step, repo_steps(fs, reached, package_reached, ps), List.append(&2, P.Step, package_steps(ps, hs), [remote_step(repo, rm)])) 67 68 def blocked(s: P.Step) -> Bool: 69 match s: 70 case P.Blocked{_, _}: 71 True{} 72 case _: 73 False{} 74 75 def any_blocked(ss: List<&2, P.Step>) -> Bool: 76 match ss: 77 case []: 78 False{} 79 case s <> rest: 80 blocked(s) || any_blocked(rest) 81 82 def show(s: P.Step) -> String: 83 match s: 84 case P.Current{w}: 85 "current " ++ w 86 case P.Publish{n, v, _}: 87 "publish " ++ n ++ "@" ++ v 88 case P.Push{r}: 89 "push github " ++ r 90 case P.Rebuild{h, _, _}: 91 "rebuild nix-darwin " ++ h 92 case P.ServePort{p, t, _}: 93 "serve :" ++ U32.show(p) ++ " -> " ++ t 94 case P.Change{w, now, want}: 95 "change " ++ w ++ ": " ++ now ++ " -> " ++ want 96 case P.Blocked{w, why}: 97 "blocked " ++ w ++ ": " ++ why 98 99 def show_all(ss: List<&2, P.Step>) -> String: 100 match ss: 101 case []: 102 "" 103 case s <> rest: 104 show(s) ++ "\n" ++ show_all(rest)