Redesign of Builder web #18
3 changed files with 1196 additions and 277 deletions
848
lib/styles.ml
Normal file
848
lib/styles.ml
Normal file
|
@ -0,0 +1,848 @@
|
||||||
|
let static_css = Tyxml.Html.Unsafe.data {|
|
||||||
|
*,
|
||||||
|
:after,
|
||||||
|
:before {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 0 solid #e5e7eb
|
||||||
|
}
|
||||||
|
|
||||||
|
:after,
|
||||||
|
:before {
|
||||||
|
--tw-content: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
:host,
|
||||||
|
html {
|
||||||
|
line-height: 1.5;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
font-family: ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
||||||
|
font-feature-settings: normal;
|
||||||
|
font-variation-settings: normal;
|
||||||
|
-webkit-tap-highlight-color: transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark\:bg-black-molly {
|
||||||
|
&:where(.dark, .dark *) {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: #0B0D12 !important;
|
||||||
|
color: #FFFFFF !important
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark\:text-gray-50 {
|
||||||
|
&:where(.dark, .dark *) {
|
||||||
|
color: #FFFFFF !important
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#robur-logo {
|
||||||
|
transform: translate(-20%, 100%) rotate(270deg);
|
||||||
|
width: 60em;
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
#robur-logo {
|
||||||
|
display: none !important; /* Hide on small screens */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
display: flex;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li::before {
|
||||||
|
content: "→";
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul li:first-child::before {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
padding: .5em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
line-height: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
height: 0;
|
||||||
|
color: inherit;
|
||||||
|
border-top-width: 1px
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr:where([title]) {
|
||||||
|
-webkit-text-decoration: underline dotted;
|
||||||
|
text-decoration: underline dotted
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder
|
||||||
|
}
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
pre,
|
||||||
|
samp {
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||||
|
font-feature-settings: normal;
|
||||||
|
font-variation-settings: normal;
|
||||||
|
font-size: 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
text-indent: 0;
|
||||||
|
border-color: inherit;
|
||||||
|
border-collapse: collapse;
|
||||||
|
table-layout: auto;
|
||||||
|
min-width: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 1rem
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(54 156 140/var(--tw-text-opacity));
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: inherit;
|
||||||
|
font-feature-settings: inherit;
|
||||||
|
font-variation-settings: inherit;
|
||||||
|
font-size: 100%;
|
||||||
|
font-weight: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
letter-spacing: inherit;
|
||||||
|
color: inherit;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input:where([type=button]),
|
||||||
|
input:where([type=reset]),
|
||||||
|
input:where([type=submit]) {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
background-color: initial;
|
||||||
|
background-image: none
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote,
|
||||||
|
dd,
|
||||||
|
dl,
|
||||||
|
figure,
|
||||||
|
hr,
|
||||||
|
p,
|
||||||
|
pre {
|
||||||
|
margin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
margin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset,
|
||||||
|
legend {
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
menu,
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog {
|
||||||
|
padding: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
[role=button],
|
||||||
|
button {
|
||||||
|
cursor: pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
:disabled {
|
||||||
|
cursor: default
|
||||||
|
}
|
||||||
|
|
||||||
|
audio,
|
||||||
|
canvas,
|
||||||
|
embed,
|
||||||
|
iframe,
|
||||||
|
img,
|
||||||
|
object,
|
||||||
|
svg,
|
||||||
|
video {
|
||||||
|
display: block;
|
||||||
|
vertical-align: middle
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
video {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrap {
|
||||||
|
text-wrap: wrap;
|
||||||
|
word-break: normal;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width:640px) {
|
||||||
|
.container {
|
||||||
|
max-width: 640px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width:768px) {
|
||||||
|
.container {
|
||||||
|
max-width: 768px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width:1024px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1024px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width:1280px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1280px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width:1536px) {
|
||||||
|
.container {
|
||||||
|
max-width: 1536px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(54 156 140/var(--tw-text-opacity));
|
||||||
|
text-decoration: underline;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||||
|
margin-top: .5rem;
|
||||||
|
margin-bottom: .5rem
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-red {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(255 78 51/var(--tw-text-opacity));
|
||||||
|
text-decoration: underline;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||||
|
margin-top: .5rem;
|
||||||
|
margin-bottom: .5rem
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-span-1 {
|
||||||
|
grid-column: span 1/span 1
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-span-3 {
|
||||||
|
grid-column: span 3/span 3
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-auto {
|
||||||
|
margin: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx-auto {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-2 {
|
||||||
|
margin-top: .5rem;
|
||||||
|
margin-bottom: .5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-4 {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 1rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
display: block
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-block {
|
||||||
|
display: inline-block
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-flex {
|
||||||
|
display: inline-flex
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-full {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.min-w-full {
|
||||||
|
min-width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-none {
|
||||||
|
flex: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-collapse {
|
||||||
|
border-collapse: collapse
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor-pointer {
|
||||||
|
cursor: pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-disc {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-inside {
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cols-2 {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr))
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cols-3 {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr))
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col {
|
||||||
|
flex-direction: column
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-start {
|
||||||
|
align-items: flex-start
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-end {
|
||||||
|
align-items: flex-end
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-center {
|
||||||
|
align-items: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-center {
|
||||||
|
justify-content: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-between {
|
||||||
|
justify-content: space-between
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-items-center {
|
||||||
|
justify-items: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap-4 {
|
||||||
|
gap: 1rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap-8 {
|
||||||
|
gap: 2rem
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.space-x-2>:not([hidden])~:not([hidden]) {
|
||||||
|
--tw-space-x-reverse: 0;
|
||||||
|
margin-right: calc(.5rem*var(--tw-space-x-reverse));
|
||||||
|
margin-left: calc(.5rem*(1 - var(--tw-space-x-reverse)))
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-x-4>:not([hidden])~:not([hidden]) {
|
||||||
|
--tw-space-x-reverse: 0;
|
||||||
|
margin-right: calc(1rem*var(--tw-space-x-reverse));
|
||||||
|
margin-left: calc(1rem*(1 - var(--tw-space-x-reverse)))
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-y-2>:not([hidden])~:not([hidden]) {
|
||||||
|
--tw-space-y-reverse: 0;
|
||||||
|
margin-top: calc(.5rem*(1 - var(--tw-space-y-reverse)));
|
||||||
|
margin-bottom: calc(.5rem*var(--tw-space-y-reverse))
|
||||||
|
}
|
||||||
|
|
||||||
|
.divide-y>:not([hidden])~:not([hidden]) {
|
||||||
|
--tw-divide-y-reverse: 0;
|
||||||
|
border-top-width: calc(1px*(1 - var(--tw-divide-y-reverse)));
|
||||||
|
border-bottom-width: calc(1px*var(--tw-divide-y-reverse))
|
||||||
|
}
|
||||||
|
|
||||||
|
.divide-gray-200>:not([hidden])~:not([hidden]) {
|
||||||
|
--tw-divide-opacity: 1;
|
||||||
|
border-color: rgb(229 231 235/var(--tw-divide-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.divide-gray-600>:not([hidden])~:not([hidden]) {
|
||||||
|
--tw-divide-opacity: 1;
|
||||||
|
border-color: rgba(75, 85, 99, var(--tw-divide-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded {
|
||||||
|
border-radius: .25rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-2xl {
|
||||||
|
border-radius: 1rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-3xl {
|
||||||
|
border-radius: 1.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-full {
|
||||||
|
border-radius: 9999px
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-lg {
|
||||||
|
border-radius: .5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-md {
|
||||||
|
border-radius: .375rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.rounded-xl {
|
||||||
|
border-radius: .75rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.border {
|
||||||
|
border-width: 1px
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-t-1 {
|
||||||
|
border-top-width: 1px
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-black-molly {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: #0B0D12;
|
||||||
|
color: #FFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gray-100 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(243 244 246/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gray-50 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(249 250 251/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gray-500\/25 {
|
||||||
|
background-color: #6b728040
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-primary-500 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(54 156 140/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-primary-800 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(32 79 74/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-secondary-500 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(255 78 51/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-secondary-700 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(200 38 13/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-10 {
|
||||||
|
padding: 2.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-2 {
|
||||||
|
padding: .5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-4 {
|
||||||
|
padding: 1rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.px-2 {
|
||||||
|
padding-left: .5rem;
|
||||||
|
padding-right: .5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.px-4 {
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.py-2 {
|
||||||
|
padding-top: .5rem;
|
||||||
|
padding-bottom: .5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.py-4 {
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-right {
|
||||||
|
text-align: right
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-2xl {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 2rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-4xl {
|
||||||
|
font-size: 2.25rem;
|
||||||
|
line-height: 2.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-7xl {
|
||||||
|
font-size: 4.5rem;
|
||||||
|
line-height: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-lg {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
line-height: 1.75rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-sm {
|
||||||
|
font-size: .875rem;
|
||||||
|
line-height: 1.25rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-xl {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 1.75rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-bold {
|
||||||
|
font-weight: 700
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-semibold {
|
||||||
|
font-weight: 600
|
||||||
|
}
|
||||||
|
|
||||||
|
.uppercase {
|
||||||
|
text-transform: uppercase
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray-300 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(209 213 219/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray-50 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(249 250 251/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-gray-800 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(31 41 55/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-primary-500 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(54 156 140/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-primary-800 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(32 79 74/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.text-secondary-500 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(255 78 51/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-secondary-700 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(200 38 13/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-md {
|
||||||
|
--tw-shadow: 0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;
|
||||||
|
--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color)
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-0 {
|
||||||
|
outline-width: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.ring-primary-200 {
|
||||||
|
--tw-ring-opacity: 1;
|
||||||
|
--tw-ring-color: rgb(171 228 214/var(--tw-ring-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:border-primary-200:hover {
|
||||||
|
--tw-border-opacity: 1;
|
||||||
|
border-color: rgb(171 228 214/var(--tw-border-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:border-transparent:hover {
|
||||||
|
border-color: #0000
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:bg-gray-200:hover {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(229 231 235/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:bg-primary-100:hover {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(213 242 235/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:bg-primary-700:hover {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(35 98 90/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:bg-primary-800:hover {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(32 79 74/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:bg-secondary-100:hover {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(255 227 223/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:bg-secondary-700:hover {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(200 38 13/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:bg-secondary-800:hover {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(165 35 15/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:bg-opacity-50:hover {
|
||||||
|
--tw-bg-opacity: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:font-bold:hover {
|
||||||
|
font-weight: 700
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:text-gray-50:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(249 250 251/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:text-primary-400:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(78 179 161/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:text-primary-50:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(243 250 249/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:text-primary-500:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(54 156 140/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:text-primary-700:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(35 98 90/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:text-primary-800:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(32 79 74/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:text-secondary-800:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(165 35 15/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion:reduce) {
|
||||||
|
.motion-reduce\:transition-opacity {
|
||||||
|
transition-property: opacity;
|
||||||
|
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
|
||||||
|
transition-duration: .15s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width:768px) {
|
||||||
|
.md\:block {
|
||||||
|
display: block
|
||||||
|
}
|
||||||
|
|
||||||
|
.md\:flex {
|
||||||
|
display: flex
|
||||||
|
}
|
||||||
|
|
||||||
|
.md\:grid {
|
||||||
|
display: grid
|
||||||
|
}
|
||||||
|
|
||||||
|
.md\:grid-cols-3 {
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr))
|
||||||
|
}
|
||||||
|
|
||||||
|
.md\:grid-cols-4 {
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr))
|
||||||
|
}
|
||||||
|
|
||||||
|
.md\:grid-cols-5 {
|
||||||
|
grid-template-columns: repeat(5, minmax(0, 1fr))
|
||||||
|
}
|
||||||
|
|
||||||
|
.md\:col-span-1 {
|
||||||
|
grid-column: span 1/span 1
|
||||||
|
}
|
||||||
|
|
||||||
|
.md\:col-span-3 {
|
||||||
|
grid-column: span 3/span 3
|
||||||
|
}
|
||||||
|
|
||||||
|
.md\:text-7xl {
|
||||||
|
font-size: 4.5rem;
|
||||||
|
line-height: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme:dark) {
|
||||||
|
.dark\:border-gray-700 {
|
||||||
|
--tw-border-opacity: 1;
|
||||||
|
border-color: rgb(55 65 81/var(--tw-border-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark\:bg-gray-900 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(17 24 39/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark\:text-gray-300 {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(209 213 219/var(--tw-text-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark\:after\:bg-gray-300:after {
|
||||||
|
content: var(--tw-content);
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(209 213 219/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.peer:checked~.dark\:peer-checked\:bg-primary-500 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(54 156 140/var(--tw-bg-opacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
.peer:focus~.dark\:peer-focus\:outline-gray-300 {
|
||||||
|
outline-color: #d1d5db
|
||||||
|
}
|
||||||
|
|
||||||
|
.peer:focus:checked~.dark\:peer-focus\:peer-checked\:outline-primary-500 {
|
||||||
|
outline-color: #369c8c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|}
|
588
lib/views.ml
588
lib/views.ml
|
@ -11,13 +11,13 @@ let check_icon result =
|
||||||
match result with
|
match result with
|
||||||
| Builder.Exited 0 ->
|
| Builder.Exited 0 ->
|
||||||
H.span ~a:H.[
|
H.span ~a:H.[
|
||||||
a_style "color: green; cursor: pointer;";
|
a_class ["text-primary-500 cursor-pointer"];
|
||||||
a_titlef "%a" Builder.pp_execution_result result;
|
a_titlef "%a" Builder.pp_execution_result result;
|
||||||
]
|
]
|
||||||
[H.txt "☑"]
|
[H.txt "☑"]
|
||||||
| _ ->
|
| _ ->
|
||||||
H.span ~a:H.[
|
H.span ~a:H.[
|
||||||
a_style "color: red; cursor: pointer;";
|
a_class ["text-secondary-500 cursor-pointer"];
|
||||||
a_titlef "%a" Builder.pp_execution_result result;
|
a_titlef "%a" Builder.pp_execution_result result;
|
||||||
]
|
]
|
||||||
[H.txt "☒"]
|
[H.txt "☒"]
|
||||||
|
@ -32,52 +32,6 @@ type nav = [
|
||||||
let pp_platform =
|
let pp_platform =
|
||||||
Fmt.(option ~none:(any "") (append (any "on ") string))
|
Fmt.(option ~none:(any "") (append (any "on ") string))
|
||||||
|
|
||||||
let static_css = Tyxml.Html.Unsafe.data {|
|
|
||||||
body {
|
|
||||||
margin: 40px auto;
|
|
||||||
line-height: 1.6;
|
|
||||||
color: #444;
|
|
||||||
background: rgb(200,200,200);
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
nav ul {
|
|
||||||
display: flex;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
nav ul li::before {
|
|
||||||
content: "→";
|
|
||||||
}
|
|
||||||
nav ul li:first-child::before {
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
nav a {
|
|
||||||
padding: .5em 1em;
|
|
||||||
}
|
|
||||||
h1,h2,h3{line-height:1.2}
|
|
||||||
.output-ts {
|
|
||||||
white-space: nowrap;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.output-ts a {text-decoration: none;}
|
|
||||||
.output-ts a:hover {text-decoration: underline;}
|
|
||||||
.output-code {
|
|
||||||
overflow: visible;
|
|
||||||
white-space: pre;
|
|
||||||
}
|
|
||||||
.toggleable {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.toggleable-descr {
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: underline;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
:checked + .toggleable {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|}
|
|
||||||
|
|
||||||
let make_breadcrumbs nav =
|
let make_breadcrumbs nav =
|
||||||
let to_nav kvs =
|
let to_nav kvs =
|
||||||
H.nav [
|
H.nav [
|
||||||
|
@ -131,35 +85,95 @@ let make_breadcrumbs nav =
|
||||||
let layout
|
let layout
|
||||||
?include_static_css
|
?include_static_css
|
||||||
?(nav=`Default)
|
?(nav=`Default)
|
||||||
?(manual_width=false)
|
|
||||||
~title
|
~title
|
||||||
body
|
body
|
||||||
=
|
=
|
||||||
let breadcrumb = make_breadcrumbs nav in
|
let breadcrumb = make_breadcrumbs nav in
|
||||||
(*> Note: Last declared CSS wins - so one can override here*)
|
(*> Note: Last declared CSS wins - so one can override here*)
|
||||||
let static_css = static_css :: Option.to_list include_static_css
|
let static_css = Styles.static_css :: Option.to_list include_static_css
|
||||||
in
|
|
||||||
let body =
|
|
||||||
let style_grid_container = H.a_style "\
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-width: 83em;
|
|
||||||
"
|
|
||||||
and style_grid = H.a_style @@
|
|
||||||
if manual_width then "" else "\
|
|
||||||
width: 76%;\
|
|
||||||
"
|
|
||||||
in
|
|
||||||
[ H.div ~a:[ style_grid_container ]
|
|
||||||
[ H.div ~a:[ style_grid ] body ]]
|
|
||||||
in
|
in
|
||||||
H.html
|
H.html
|
||||||
(H.head (H.title (H.txt title))
|
(H.head (H.title (H.txt title))
|
||||||
[H.style ~a:H.[a_mime_type "text/css"] static_css])
|
[
|
||||||
PixieDust marked this conversation as resolved
Outdated
|
|||||||
(H.body [
|
H.meta ~a:[ H.a_charset "UTF-8" ] ();
|
||||||
breadcrumb;
|
H.meta ~a:[H.a_name "viewport"; H.a_content "width=device-width, initial-scale=1.0";]();
|
||||||
H.main body
|
H.style ~a:H.[a_mime_type "text/css"] static_css])
|
||||||
|
(H.body ~a:[H.a_class ["bg-gray-50 dark:bg-black-molly w-full text-gray-800 dark:text-gray-50 mx-auto p-10 md:grid md:grid-cols-4"]] [
|
||||||
|
H.div ~a:[H.a_class ["text-center md:col-span-1 hidden md:block"]; H.a_style ""] [
|
||||||
|
H.img ~a:[H.a_id "robur-logo"] ~src:"https://i.ibb.co/Y4YsvcDb/robur-logo.png" ~alt:"Robur Logo" ()
|
||||||
|
];
|
||||||
|
H.div ~a:[H.a_class ["mx-auto w-full md:col-span-3 px-4"]] [
|
||||||
|
H.div ~a:[H.a_class ["md:flex justify-between items-center"]] [
|
||||||
|
H.div [breadcrumb];
|
||||||
|
H.div ~a:[H.a_class ["flex items-center space-x-4"]] [
|
||||||
|
H.form ~a:[H.a_action "/hash"; H.a_method `Get; H.a_class ["my-4 p-4"]] [
|
||||||
|
H.label ~a:[H.a_class ["block text-lg font-semibold my-2 text-right"]] [
|
||||||
|
H.txt "Search artifact by SHA256";
|
||||||
|
];
|
||||||
|
H.div ~a:[H.a_class ["w-full flex space-x-2 justify-end justify-items-center items-center"]] [
|
||||||
|
H.div [
|
||||||
|
H.input ~a:[
|
||||||
|
H.a_input_type `Search;
|
||||||
|
H.a_id "sha256";
|
||||||
|
H.a_required ();
|
||||||
|
H.a_name "sha256";
|
||||||
|
H.a_class ["w-full border bg-gray-200 text-gray-800 rounded px-3 py-2 focus:ring-0 focus:ring-primary-200"]
|
||||||
|
] ()
|
||||||
|
];
|
||||||
|
H.div ~a:[H.a_class ["text-center"]] [
|
||||||
|
H.input ~a:[
|
||||||
|
H.a_input_type `Submit;
|
||||||
|
H.a_value "Search";
|
||||||
reynir
commented
Do we need this many css classes? I don't even know what they mean. Do we need this many css classes? I don't even know what they mean.
|
|||||||
|
H.a_class ["my-4 bg-primary-500 text-gray-50 cursor-pointer font-bold py-2 px-4 rounded hover:bg-primary-800"]
|
||||||
|
] ()
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
H.button ~a:[
|
||||||
|
H.a_id "theme-toggle";
|
||||||
|
H.a_class ["p-2 rounded-full border border-gray-300 bg-gray-100 dark:bg-gray-700 dark:text-white hover:bg-gray-200 dark:hover:bg-gray-600"];
|
||||||
|
] [
|
||||||
|
H.txt "🌙"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
H.main body
|
||||||
|
];
|
||||||
|
|
||||||
|
H.script
|
||||||
|
(H.txt "
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const themeToggle = document.getElementById('theme-toggle');
|
||||||
|
const html = document.documentElement;
|
||||||
|
const logo = document.getElementById('robur-logo'); // Ensure the image has this ID
|
||||||
|
|
||||||
|
function updateTheme() {
|
||||||
|
const isDark = html.classList.contains('dark');
|
||||||
|
themeToggle.innerText = isDark ? '☀️' : '🌑';
|
||||||
|
logo.src = isDark
|
||||||
|
? 'https://i.ibb.co/Y4YsvcDb/robur-logo.png' // Dark mode logo
|
||||||
|
: 'https://i.ibb.co/r2DRDdTt/robur-logo-black-writing.png'; // Light mode logo
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTheme() {
|
||||||
|
html.classList.toggle('dark');
|
||||||
|
localStorage.setItem('theme', html.classList.contains('dark') ? 'dark' : 'light');
|
||||||
|
updateTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load user preference from localStorage
|
||||||
|
if (localStorage.getItem('theme') === 'dark') {
|
||||||
reynir
commented
I think this can also be achieved with css. I think this can also be achieved with css.
|
|||||||
|
html.classList.add('dark');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set correct icon and logo on load
|
||||||
|
updateTheme();
|
||||||
|
|
||||||
|
// Attach event listener (in case onclick fails)
|
||||||
|
themeToggle.addEventListener('click', toggleTheme);
|
||||||
|
});
|
||||||
|
")
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
let toggleable ?(hidden=true) ~id ~description content =
|
let toggleable ?(hidden=true) ~id ~description content =
|
||||||
|
@ -196,14 +210,14 @@ let artifact
|
||||||
~build:build.Builder_db.Build.uuid
|
~build:build.Builder_db.Build.uuid
|
||||||
~artifact:(`File filepath) ()
|
~artifact:(`File filepath) ()
|
||||||
in
|
in
|
||||||
[
|
H.div [
|
||||||
H.a ~a:H.[a_href artifact_link] [
|
H.a ~a:[H.a_href artifact_link; H.a_class ["link"]] [
|
||||||
if basename then H.txt (Fpath.basename filepath)
|
(if basename then H.txt ("Download " ^ Fpath.basename filepath)
|
||||||
else txtf "%a" Fpath.pp filepath
|
else txtf "Download %a" Fpath.pp filepath);
|
||||||
|
txtf " (%a)" Fmt.byte_size size;
|
||||||
];
|
];
|
||||||
H.txt " ";
|
H.br ();
|
||||||
H.code [txtf "SHA256:%s" (Ohex.encode sha256)];
|
H.p ~a:[H.a_class ["wrap"]] [txtf "SHA256:%s" (Ohex.encode sha256)];
|
||||||
txtf " (%a)" Fmt.byte_size size;
|
|
||||||
]
|
]
|
||||||
|
|
||||||
let page_not_found ~target ~referer =
|
let page_not_found ~target ~referer =
|
||||||
|
@ -236,7 +250,7 @@ let viz_not_found =
|
||||||
[ txtf "%s" title ];
|
[ txtf "%s" title ];
|
||||||
]
|
]
|
||||||
in
|
in
|
||||||
let static_css = static_css :: [ Tyxml.Html.Unsafe.data "\
|
let static_css = Styles.static_css :: [ Tyxml.Html.Unsafe.data "\
|
||||||
body { background: rgb(191,191,191); }\
|
body { background: rgb(191,191,191); }\
|
||||||
"]
|
"]
|
||||||
in
|
in
|
||||||
|
@ -251,128 +265,161 @@ let viz_not_found =
|
||||||
|
|
||||||
module Builds = struct
|
module Builds = struct
|
||||||
|
|
||||||
let data =
|
|
||||||
{|
|
|
||||||
# Reproducible OPAM builds
|
|
||||||
|
|
||||||
This website offers binary MirageOS unikernels and supplementary OS packages.
|
|
||||||
If you want to use our binary packages and setup unikernels, follow
|
|
||||||
[these instructions](https://robur.coop/Projects/Reproducible_builds).
|
|
||||||
The unikernels are statically linked executables where the execution target is
|
|
||||||
independent of the build platform - so even if they're compiled on a FreeBSD
|
|
||||||
system they can be run on a Linux or OpenBSD host. Many are executed using a
|
|
||||||
[solo5](https://github.com/solo5/solo5) tender.
|
|
||||||
The filename suffix of the unikernel binary indicate the expected execution environment:
|
|
||||||
- `.hvt`: hardware virtualized - requires `solo5-hvt`
|
|
||||||
([Linux KVM](https://www.linux-kvm.org/page/Main_Page),
|
|
||||||
[FreeBSD BHyve](https://wiki.freebsd.org/bhyve),
|
|
||||||
[OpenBSD VMM](https://man.openbsd.org/vmm)),
|
|
||||||
- `.spt`: sandboxed process - requires `solo5-spt` (Linux with seccomp),
|
|
||||||
- `.xen`: Xen PVH virtual machine (on a Xen or QubesOS host),
|
|
||||||
- `.virtio`: any virtio environment (qemu, GCE, KVM, BHyve),
|
|
||||||
- `.muen`: on [muen](https://muen.sk).
|
|
||||||
|
|
||||||
A persistent link to the latest successful build is available as
|
|
||||||
`/job/*jobname*/build/latest/`. Each build can be reproduced with
|
|
||||||
[orb](https://github.com/robur-coop/orb/). The builds are scheduled and executed
|
|
||||||
daily by [builder](https://github.com/robur-coop/builder/). This web interface is
|
|
||||||
[builder-web](https://git.robur.coop/robur/builder-web/). Read further information
|
|
||||||
[on our project page](https://robur.coop/Projects/Reproducible_builds). This
|
|
||||||
work has been funded by the European Union under the
|
|
||||||
[NGI Pointer](https://pointer.ngi.eu) program. Contact team ATrobur.coop if you
|
|
||||||
have questions or suggestions.
|
|
||||||
|}
|
|
||||||
|
|
||||||
let make_header =
|
let make_header =
|
||||||
[
|
H.[
|
||||||
H.Unsafe.data (Utils.md_to_html data);
|
div ~a:[a_class ["header container mx-auto px-4 py-8 text-gray-800 w-full"]] [
|
||||||
reynir
commented
Why not keep the markdown? Why not keep the markdown?
PixieDust
commented
This is so that I can style the text in different ways. I use a grid to do this This is so that I can style the text in different ways. I use a grid to do this
|
|||||||
H.form ~a:H.[a_action "/hash"; a_method `Get] [
|
(* Logo Section *)
|
||||||
H.label [
|
div ~a:[a_class ["flex items-center my-4"]] [
|
||||||
H.txt "Search artifact by SHA256";
|
];
|
||||||
H.br ();
|
h1 ~a:[a_class ["md:text-7xl text-4xl font-bold text-primary-500 text-center"]]
|
||||||
H.input ~a:H.[
|
[txt "Reproducible OPAM Builds"]
|
||||||
a_input_type `Search;
|
|
||||||
a_id "sha256";
|
|
||||||
a_name "sha256";
|
|
||||||
] ();
|
|
||||||
];
|
];
|
||||||
H.input ~a:H.[
|
|
||||||
a_input_type `Submit;
|
|
||||||
a_value "Search";
|
|
||||||
] ();
|
|
||||||
];
|
|
||||||
]
|
|
||||||
|
|
||||||
let make_platform_builds ~job_name (platform, latest_build, latest_artifact) =
|
div ~a:[a_class ["md:grid grid-cols-2 gap-4"]] [
|
||||||
[
|
div [
|
||||||
check_icon latest_build.Builder_db.Build.result;
|
p ~a:[a_class ["text-lg my-4"]] [
|
||||||
H.txt " ";
|
txt "This website offers binary MirageOS unikernels and supplementary OS packages. ";
|
||||||
H.a ~a:[
|
txt "If you want to use our binary packages and setup unikernels, follow ";
|
||||||
H.a_href @@ Link.Job.make ~job_name
|
a ~a:[a_href "https://robur.coop/Projects/Reproducible_builds"; a_class ["link"]]
|
||||||
~queries:[ `Platform platform ] ()
|
[txt "these instructions"];
|
||||||
|
txt "."
|
||||||
|
];
|
||||||
|
p ~a:[a_class ["text-lg my-4"]] [
|
||||||
|
txt "The unikernels are statically linked executables where the execution target is ";
|
||||||
|
txt "independent of the build platform - so even if they're compiled on a FreeBSD ";
|
||||||
|
txt "system they can be run on a Linux or OpenBSD host. Many are executed using a ";
|
||||||
|
a ~a:[a_href "https://github.com/solo5/solo5"; a_class ["link"]]
|
||||||
|
[txt "solo5"];
|
||||||
|
txt " tender."
|
||||||
|
];
|
||||||
|
];
|
||||||
|
div [
|
||||||
|
p ~a:[a_class ["text-lg my-4"]] [
|
||||||
|
txt "A persistent link to the latest successful build is available as ";
|
||||||
|
code ~a:[a_class ["px-2 py-1 rounded text-sm text-primary-500"]]
|
||||||
|
[txt "/job/*jobname*/build/latest/"];
|
||||||
|
txt ". Each build can be reproduced with ";
|
||||||
|
a ~a:[a_href "https://github.com/robur-coop/orb/"; a_class ["link"]]
|
||||||
|
[txt "orb"];
|
||||||
|
txt "."
|
||||||
|
];
|
||||||
|
p ~a:[a_class ["text-lg my-4"]] [
|
||||||
|
txt "The builds are scheduled and executed daily by ";
|
||||||
|
a ~a:[a_href "https://github.com/robur-coop/builder/"; a_class ["link"]]
|
||||||
|
[txt "builder"];
|
||||||
|
txt ". This web interface is ";
|
||||||
|
a ~a:[a_href "https://git.robur.coop/robur/builder-web/"; a_class ["link"]]
|
||||||
|
[txt "builder-web"];
|
||||||
|
txt ". Read further information ";
|
||||||
|
a ~a:[a_href "https://robur.coop/Projects/Reproducible_builds"; a_class ["link"]]
|
||||||
|
[txt "on our project page"];
|
||||||
|
txt "."
|
||||||
|
];
|
||||||
|
p ~a:[a_class ["text-lg my-4"]] [
|
||||||
|
txt "This work has been funded by the European Union under the ";
|
||||||
|
a ~a:[a_href "https://pointer.ngi.eu"; a_class ["link"]]
|
||||||
|
[txt "NGI Pointer"];
|
||||||
|
txt " program. Contact team AT robur.coop if you have questions or suggestions."
|
||||||
|
];
|
||||||
|
];
|
||||||
|
];
|
||||||
|
div ~a:[a_class ["my-4"]] [
|
||||||
|
h2
|
||||||
|
[txt "Execution Environments"];
|
||||||
|
ul ~a:[a_class ["list-disc list-inside text-lg space-y-2"]] [
|
||||||
|
li [span ~a:[a_class ["text-primary-500"]] [txt ".spt: "]; txt "sandboxed process - requires solo5-spt (Linux with seccomp)"];
|
||||||
|
li [span ~a:[a_class ["text-primary-500"]] [txt ".xen: "]; txt "Xen PVH virtual machine (on a Xen or QubesOS host)"];
|
||||||
|
li [span ~a:[a_class ["text-primary-500"]] [txt ".virtio: "]; txt "any virtio environment (qemu, GCE, KVM, BHyve)"];
|
||||||
|
li [span ~a:[a_class ["text-primary-500"]] [txt ".muem: "]; txt "on muen"]
|
||||||
|
];
|
||||||
|
]
|
||||||
]
|
]
|
||||||
[H.txt platform];
|
|
||||||
H.txt " ";
|
|
||||||
H.a ~a:[
|
|
||||||
H.a_href @@ Link.Job_build.make
|
|
||||||
~job_name
|
|
||||||
~build:latest_build.Builder_db.Build.uuid ()]
|
|
||||||
[txtf "%a" pp_ptime latest_build.Builder_db.Build.start];
|
|
||||||
H.txt " ";
|
|
||||||
|
|
||||||
]
|
|
||||||
@ artifact
|
|
||||||
~basename:true
|
|
||||||
~job_name
|
|
||||||
~build:latest_build
|
|
||||||
~file:latest_artifact
|
|
||||||
@ [ H.br () ]
|
|
||||||
|
|
||||||
let make_jobs jobs =
|
let make_platform_builds ~job_name (platform, latest_build, latest_artifact) =
|
||||||
jobs |> List.map (fun (job_name, synopsis, platform_builds) ->
|
H.[
|
||||||
H.li (
|
div ~a:[a_class ["md:grid grid-cols-3 space-y-2 p-2 rounded-lg"]]
|
||||||
[
|
[
|
||||||
H.a ~a:H.[a_href ("/job/" ^ job_name ^ "/")]
|
div ~a:[a_class ["flex items-center space-x-2"]]
|
||||||
[H.txt job_name];
|
[
|
||||||
H.br ();
|
check_icon latest_build.Builder_db.Build.result;
|
||||||
H.txt (Option.value ~default:"" synopsis);
|
a ~a:[
|
||||||
H.br ()
|
a_href @@ Link.Job.make ~job_name ~queries:[ `Platform platform ] ();
|
||||||
]
|
a_class ["link font-medium"]
|
||||||
@ List.concat_map (make_platform_builds ~job_name) platform_builds
|
]
|
||||||
)
|
[txt platform]
|
||||||
)
|
];
|
||||||
|
div ~a:[a_class ["text-gray-300"]]
|
||||||
|
[ a ~a:[
|
||||||
|
a_href @@ Link.Job_build.make
|
||||||
|
~job_name
|
||||||
|
~build:latest_build.Builder_db.Build.uuid ();
|
||||||
|
a_class ["link"]
|
||||||
|
]
|
||||||
|
[txtf "%a" pp_ptime latest_build.Builder_db.Build.start]; ];
|
||||||
|
|
||||||
|
div ~a:[a_class [""]]
|
||||||
|
[ artifact ~basename:true ~job_name ~build:latest_build ~file:latest_artifact ];
|
||||||
|
];
|
||||||
|
]
|
||||||
|
|
||||||
|
let make_jobs jobs =
|
||||||
|
H.div
|
||||||
|
~a:[H.a_class ["min-w-full"]]
|
||||||
|
(List.map (fun (job_name, synopsis, platform_builds) ->
|
||||||
|
H.div ~a:[H.a_class ["md:grid md:grid-cols-4 divide-y dark:divide-gray-200 divide-gray-600"]]
|
||||||
|
[
|
||||||
|
H.div
|
||||||
|
~a:[H.a_class ["px-6 py-4 font-medium md:col-span-1"]]
|
||||||
|
[
|
||||||
|
H.a ~a:[H.a_href ("/job/" ^ job_name ^ "/"); H.a_class ["link font-bold"]]
|
||||||
|
[H.txt job_name];
|
||||||
|
H.br();
|
||||||
|
H.txt (Option.value ~default:"" synopsis);
|
||||||
|
];
|
||||||
|
|
||||||
|
H.div
|
||||||
|
~a:[H.a_class ["px-4 py-4 text-gray-400 block md:col-span-3"]]
|
||||||
|
[
|
||||||
|
H.div ~a:[H.a_class ["md:flex flex-col wrap"]]
|
||||||
|
(List.concat_map (make_platform_builds ~job_name) platform_builds);
|
||||||
|
];
|
||||||
|
];
|
||||||
|
)
|
||||||
|
jobs)
|
||||||
|
|
||||||
let make_body section_job_map =
|
let make_body section_job_map =
|
||||||
let aux section jobs acc =
|
let aux section jobs acc =
|
||||||
acc @ [
|
acc @ [
|
||||||
H.h2 [ H.txt section ];
|
H.div ~a:[H.a_class ["my-4 py-4"]] [
|
||||||
H.ul (make_jobs jobs)
|
H.h2 ~a:[H.a_class ["uppercase font-bold text-2xl"]] [ H.txt section ];
|
||||||
|
make_jobs jobs;
|
||||||
|
]
|
||||||
]
|
]
|
||||||
in
|
in
|
||||||
Utils.String_map.fold aux section_job_map []
|
Utils.String_map.fold aux section_job_map []
|
||||||
|
|
||||||
let make_failed_builds =
|
let make_failed_builds =
|
||||||
[ H.p [
|
[ H.div ~a:H.[a_class ["flex justify-center my-4"]] [
|
||||||
H.txt "View the latest failed builds ";
|
H.a ~a:H.[a_href "/failed-builds";
|
||||||
H.a ~a:H.[a_href "/failed-builds"]
|
a_class ["link-red font-semibold"]]
|
||||||
[H.txt "here"];
|
[H.txt "View Latest Failed Builds"];
|
||||||
H.txt ".";
|
]]
|
||||||
]]
|
|
||||||
|
|
||||||
let make_all_or_active all =
|
let make_all_or_active all =
|
||||||
[ H.p [
|
[ H.div ~a:H.[a_class ["flex justify-center my-4"]] [
|
||||||
H.txt (if all then "View active jobs " else "View all jobs ");
|
H.a ~a:H.[a_href (if all then "/" else "/all-builds");
|
||||||
H.a ~a:H.[a_href (if all then "/" else "/all-builds")]
|
a_class ["link font-semibold"]]
|
||||||
[H.txt "here"];
|
[H.txt (if all then "View Active Jobs" else "View All Jobs")];
|
||||||
H.txt ".";
|
]]
|
||||||
]]
|
|
||||||
|
|
||||||
let make ~all section_job_map =
|
let make ~all section_job_map =
|
||||||
layout ~title:"Reproducible OPAM builds"
|
layout ~title:"Reproducible OPAM builds"
|
||||||
(make_header
|
(make_header
|
||||||
@ make_body section_job_map
|
@ make_body section_job_map
|
||||||
@ make_failed_builds
|
@ make_failed_builds
|
||||||
@ make_all_or_active all)
|
@ make_all_or_active all)
|
||||||
|
|
||||||
|
|
||||||
let make_json ~all:_ section_job_map =
|
let make_json ~all:_ section_job_map =
|
||||||
let all_jobs =
|
let all_jobs =
|
||||||
|
@ -396,39 +443,42 @@ end
|
||||||
module Job = struct
|
module Job = struct
|
||||||
|
|
||||||
let make_header ~job_name ~platform ~readme =
|
let make_header ~job_name ~platform ~readme =
|
||||||
H.h1 [txtf "Job %s %a" job_name pp_platform platform]
|
H.h1 ~a:[H.a_class ["text-4xl font-bold text-center my-4 py-4"]] [txtf "Job %s %a" job_name pp_platform platform]
|
||||||
:: (
|
:: (
|
||||||
match readme with
|
match readme with
|
||||||
| None -> []
|
| None -> []
|
||||||
| Some data ->
|
| Some data ->
|
||||||
[
|
[
|
||||||
H.h2 ~a:H.[a_id "readme"] [H.txt "README"];
|
H.div ~a:[H.a_class ["flex justify-between items-center"]] [
|
||||||
H.a ~a:H.[a_href "#builds"] [H.txt "Skip to builds"];
|
H.h2 ~a:[H.a_id "readme";] [H.txt "README"];
|
||||||
|
H.a ~a:[H.a_href "#builds"; H.a_class ["link"]] [H.txt "Skip to builds"];
|
||||||
|
];
|
||||||
H.Unsafe.data (Utils.md_to_html ~adjust_heading:2 data)
|
H.Unsafe.data (Utils.md_to_html ~adjust_heading:2 data)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
let make_build ~job_name (build, main_binary) =
|
let make_build ~job_name (build, main_binary) =
|
||||||
H.li (
|
H.li ~a:[H.a_class ["my-4 p-4 border-t-1"]] (
|
||||||
[
|
[
|
||||||
check_icon build.Builder_db.Build.result;
|
H.div ~a:[H.a_class ["flex my-2"]] [
|
||||||
txtf " %s " build.platform;
|
check_icon build.Builder_db.Build.result;
|
||||||
H.a ~a:H.[
|
H.p ~a:[H.a_class ["text-xl px-2"]] [txtf " %s " build.platform;];
|
||||||
a_href @@ Link.Job_build.make
|
];
|
||||||
|
H.a ~a:[
|
||||||
|
H.a_href @@ Link.Job_build.make
|
||||||
~job_name
|
~job_name
|
||||||
~build:build.Builder_db.Build.uuid () ]
|
~build:build.Builder_db.Build.uuid (); H.a_class ["link"] ]
|
||||||
[
|
[
|
||||||
txtf "%a" pp_ptime build.Builder_db.Build.start;
|
txtf "%a" pp_ptime build.Builder_db.Build.start;
|
||||||
];
|
];
|
||||||
H.txt " ";
|
|
||||||
]
|
]
|
||||||
@ match main_binary with
|
@ match main_binary with
|
||||||
| Some main_binary ->
|
| Some main_binary ->
|
||||||
artifact
|
[artifact
|
||||||
~basename:true
|
~basename:true
|
||||||
~job_name
|
~job_name
|
||||||
~build
|
~build
|
||||||
~file:main_binary
|
~file:main_binary]
|
||||||
| None ->
|
| None ->
|
||||||
[ txtf "Build failure: %a" Builder.pp_execution_result
|
[ txtf "Build failure: %a" Builder.pp_execution_result
|
||||||
build.Builder_db.Build.result ]
|
build.Builder_db.Build.result ]
|
||||||
|
@ -436,8 +486,10 @@ module Job = struct
|
||||||
|
|
||||||
let make_builds ~failed ~job_name ~platform builds =
|
let make_builds ~failed ~job_name ~platform builds =
|
||||||
[
|
[
|
||||||
H.h2 ~a:H.[a_id "builds"] [H.txt "Builds"];
|
H.div ~a:[H.a_class ["flex justify-between items-center"]] [
|
||||||
H.a ~a:H.[a_href "#readme"] [H.txt "Back to readme"];
|
H.h2 ~a:[H.a_id "builds"] [H.txt "Builds"];
|
||||||
|
H.a ~a:[H.a_href "#readme"; H.a_class ["link"]] [H.txt "Back to readme"];
|
||||||
|
];
|
||||||
H.ul (builds |> List.map (make_build ~job_name));
|
H.ul (builds |> List.map (make_build ~job_name));
|
||||||
let queries =
|
let queries =
|
||||||
platform |> Option.map (fun p -> `Platform p) |> Option.to_list
|
platform |> Option.map (fun p -> `Platform p) |> Option.to_list
|
||||||
|
@ -446,7 +498,7 @@ module Job = struct
|
||||||
H.p [
|
H.p [
|
||||||
H.txt "Excluding failed builds " ;
|
H.txt "Excluding failed builds " ;
|
||||||
H.a ~a:H.[
|
H.a ~a:H.[
|
||||||
a_href @@ Link.Job.make ~job_name ~queries ()
|
a_href @@ Link.Job.make ~job_name ~queries (); a_class ["link"]
|
||||||
]
|
]
|
||||||
[H.txt "here"] ;
|
[H.txt "here"] ;
|
||||||
H.txt "." ]
|
H.txt "." ]
|
||||||
|
@ -454,7 +506,7 @@ module Job = struct
|
||||||
H.p [
|
H.p [
|
||||||
H.txt "Including failed builds " ;
|
H.txt "Including failed builds " ;
|
||||||
H.a ~a:H.[
|
H.a ~a:H.[
|
||||||
a_href @@ Link.Job.make_failed ~job_name ~queries ()
|
a_href @@ Link.Job.make_failed ~job_name ~queries (); a_class ["link-red"]
|
||||||
]
|
]
|
||||||
[H.txt "here"] ;
|
[H.txt "here"] ;
|
||||||
H.txt "." ]
|
H.txt "." ]
|
||||||
|
@ -531,16 +583,16 @@ module Job_build = struct
|
||||||
let aux (file:Builder_db.file) =
|
let aux (file:Builder_db.file) =
|
||||||
let sha256_hex = Ohex.encode file.sha256 in
|
let sha256_hex = Ohex.encode file.sha256 in
|
||||||
[
|
[
|
||||||
H.dt [
|
H.dt [
|
||||||
H.a ~a:H.[a_href @@ Link.Job_build_artifact.make
|
H.a ~a:[H.a_href @@ Link.Job_build_artifact.make
|
||||||
~job_name
|
~job_name
|
||||||
~build:build_uuid
|
~build:build_uuid
|
||||||
~artifact:(`File file.filepath) ()
|
~artifact:(`File file.filepath) ();
|
||||||
|
H.a_class ["link"]
|
||||||
]
|
]
|
||||||
[H.code [txtf "%a" Fpath.pp file.filepath]] ];
|
[H.code [txtf "%a" Fpath.pp file.filepath; txtf " (%a)" Fmt.byte_size file.size]] ];
|
||||||
H.dd ([
|
H.dd ([
|
||||||
H.code [H.txt "SHA256:"; H.txt sha256_hex];
|
H.code ~a:[H.a_class ["wrap"]] [H.txt "SHA256:"; H.txt sha256_hex];
|
||||||
txtf " (%a)" Fmt.byte_size file.size;
|
|
||||||
] @
|
] @
|
||||||
match main_binary, solo5_manifest with
|
match main_binary, solo5_manifest with
|
||||||
| Some main_binary, Some solo5_manifest when main_binary = file ->
|
| Some main_binary, Some solo5_manifest when main_binary = file ->
|
||||||
|
@ -550,7 +602,7 @@ module Job_build = struct
|
||||||
in
|
in
|
||||||
[
|
[
|
||||||
H.h3 [H.txt "Build artifacts"];
|
H.h3 [H.txt "Build artifacts"];
|
||||||
H.dl (List.concat_map aux artifacts)
|
H.dl ~a:[H.a_class ["p-4 my-4"]] (List.concat_map aux artifacts)
|
||||||
PixieDust marked this conversation as resolved
Outdated
reynir
commented
I have intentionally not used local opens as there are just too many things in Tyxml.Html that can shadow things inadverdently. I have intentionally not used local opens as there are just too many things in Tyxml.Html that can shadow things inadverdently.
|
|||||||
]
|
]
|
||||||
|
|
||||||
let make_reproductions
|
let make_reproductions
|
||||||
|
@ -581,13 +633,15 @@ module Job_build = struct
|
||||||
different_input_same_output
|
different_input_same_output
|
||||||
in
|
in
|
||||||
[
|
[
|
||||||
H.h3 [
|
H.div ~a:[H.a_class ["my-4"]] [
|
||||||
|
H.h3 [
|
||||||
txtf "Reproduced by %d builds"
|
txtf "Reproduced by %d builds"
|
||||||
(List.length (same_input_same_output @ different_input_same_output))] ;
|
(List.length (same_input_same_output @ different_input_same_output))];
|
||||||
H.ul @@ (
|
H.ul @@ (
|
||||||
same_input_same_output_html
|
same_input_same_output_html
|
||||||
@ different_input_same_output_html
|
@ different_input_same_output_html
|
||||||
)
|
)
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
let make_not_reproducible
|
let make_not_reproducible
|
||||||
|
@ -626,7 +680,7 @@ module Job_build = struct
|
||||||
H.a ~a:[
|
H.a ~a:[
|
||||||
H.a_href @@ Link.Compare_builds.make
|
H.a_href @@ Link.Compare_builds.make
|
||||||
~left:b.uuid
|
~left:b.uuid
|
||||||
~right:build.uuid () ]
|
~right:build.uuid () ; H.a_class ["link"] ]
|
||||||
[txtf "%a" pp_ptime b.start]]
|
[txtf "%a" pp_ptime b.start]]
|
||||||
]
|
]
|
||||||
| _ -> []
|
| _ -> []
|
||||||
|
@ -651,27 +705,47 @@ module Job_build = struct
|
||||||
~latest ~next ~previous
|
~latest ~next ~previous
|
||||||
=
|
=
|
||||||
[
|
[
|
||||||
H.h2 ~a:H.[a_id "build"] [txtf "Build %a" pp_ptime build.start];
|
H.h2 ~a:[H.a_id "build";] [txtf "Build %a" pp_ptime build.start];
|
||||||
H.p [txtf "Built on platform %s" build.platform ];
|
H.div ~a:[H.a_class []] [
|
||||||
H.p [txtf "Build took %a." Ptime.Span.pp delta ];
|
H.table
|
||||||
H.p [txtf "Execution result: %a." Builder.pp_execution_result build.result];
|
~thead:
|
||||||
|
(H.thead
|
||||||
|
[
|
||||||
|
H.tr ~a:[H.a_class ["border"]]
|
||||||
|
[
|
||||||
|
H.th [ H.txt "Platform" ];
|
||||||
|
H.th [ H.txt "Duration" ];
|
||||||
|
H.th [ H.txt "Execution Result" ];
|
||||||
|
];
|
||||||
|
])
|
||||||
|
[
|
||||||
|
H.tr ~a:[H.a_class ["text-center border"]]
|
||||||
|
[
|
||||||
|
H.td [ txtf "%s" build.platform ];
|
||||||
|
H.td [ txtf "%a." Ptime.Span.pp delta ];
|
||||||
|
H.td [ txtf "%a" Builder.pp_execution_result build.result ];
|
||||||
|
]
|
||||||
|
];
|
||||||
|
];
|
||||||
H.h3 [H.txt "Build info"];
|
H.h3 [H.txt "Build info"];
|
||||||
H.ul [
|
H.div ~a:[H.a_class ["my-4 md:flex justify-between items-center"]] [
|
||||||
H.li [
|
H.div [
|
||||||
H.a ~a:H.[
|
H.a ~a:[
|
||||||
a_href @@ Link.Job_build_artifact.make
|
H.a_href @@ Link.Job_build_artifact.make
|
||||||
~job_name
|
~job_name
|
||||||
~build:build.uuid
|
~build:build.uuid
|
||||||
~artifact:`Console ()
|
~artifact:`Console ();
|
||||||
] [H.txt "Console output"];
|
H.a_class ["link"]
|
||||||
|
] [H.txt "Console output -->"];
|
||||||
];
|
];
|
||||||
H.li [
|
H.div [
|
||||||
H.a ~a:H.[
|
H.a ~a:[
|
||||||
a_href @@ Link.Job_build_artifact.make
|
H.a_href @@ Link.Job_build_artifact.make
|
||||||
~job_name
|
~job_name
|
||||||
~build:build.uuid
|
~build:build.uuid
|
||||||
~artifact:`Script ()
|
~artifact:`Script ();
|
||||||
] [H.txt "Build script"];
|
H.a_class ["link"]
|
||||||
|
] [H.txt "Build script -->"];
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
]
|
]
|
||||||
|
@ -811,22 +885,17 @@ and the rest of the unaccounted data.\
|
||||||
~same_input_different_output
|
~same_input_different_output
|
||||||
~latest ~next ~previous
|
~latest ~next ~previous
|
||||||
in
|
in
|
||||||
let style_grid = H.a_style "display: flex; " in
|
|
||||||
let style_col_left =
|
|
||||||
H.a_style "width: 45em; min-width: 43em;" in
|
|
||||||
let style_col_right = H.a_style "width: 50%" in
|
|
||||||
let body = [
|
let body = [
|
||||||
H.h1 [txtf "Job %s" job_name];
|
H.h1 ~a:[H.a_class ["text-4xl font-bold text-center"]] [txtf "Job %s" job_name];
|
||||||
H.div~a:[ style_grid ] [
|
H.div~a:[ H.a_class ["md:grid grid-cols-2 gap-8"] ] [
|
||||||
H.div~a:[ style_col_left ] left_column;
|
H.div~a:[ ] left_column;
|
||||||
H.div~a:[ style_col_right ] right_column
|
H.div~a:[ ] right_column
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
in
|
in
|
||||||
layout
|
layout
|
||||||
~nav:(`Build (job_name, build))
|
~nav:(`Build (job_name, build))
|
||||||
~title:(Fmt.str "Job %s %a" job_name pp_ptime build.start)
|
~title:(Fmt.str "Job %s %a" job_name pp_ptime build.start)
|
||||||
~manual_width:true
|
|
||||||
body
|
body
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -864,10 +933,9 @@ let duniverse_diffs diffs =
|
||||||
]) diffs
|
]) diffs
|
||||||
|
|
||||||
let opam_diffs diffs =
|
let opam_diffs diffs =
|
||||||
List.concat_map (fun pd ->
|
List.concat_map (fun pd ->
|
||||||
H.h4 [ txtf "%a" Opamdiff.pp_opam_diff pd ] ::
|
H.h4 ~a:[H.a_class ["text-md text-primary-500"]] [ txtf "%a" Opamdiff.pp_opam_diff pd ] ::
|
||||||
H.h5 [ H.txt "diff" ] ::
|
H.pre [ H.code [H.txt pd.diff] ] ::
|
||||||
H.pre [ H.code [ H.txt pd.diff ] ] ::
|
|
||||||
H.br () :: [])
|
H.br () :: [])
|
||||||
diffs
|
diffs
|
||||||
|
|
||||||
|
@ -886,41 +954,41 @@ let compare_builds
|
||||||
if amount = 0 then
|
if amount = 0 then
|
||||||
items, data
|
items, data
|
||||||
else
|
else
|
||||||
H.li [ H.a ~a:[H.a_href id_href] [txtf "%d %s" amount txt] ] :: items,
|
H.li [ H.a ~a:[H.a_href id_href; H.a_class ["link"]] [txtf "%d %s" amount txt] ] :: items,
|
||||||
data @ H.h3 ~a:[H.a_id id] [H.txt txt] :: code)
|
data @ H.h3 ~a:[H.a_id id;] [H.txt txt] :: code)
|
||||||
([], [])
|
([], [])
|
||||||
([ ("opam-packages-removed", "Opam packages removed",
|
([ ("opam-packages-removed", "Opam packages removed",
|
||||||
OpamPackage.Set.cardinal left, [ H.code (packages left) ]) ;
|
OpamPackage.Set.cardinal left, [ H.code ~a:[H.a_class ["code-diff"]] (packages left) ]) ;
|
||||||
("opam-packages-installede", "New opam packages installed",
|
("opam-packages-installede", "New opam packages installed",
|
||||||
OpamPackage.Set.cardinal right, [ H.code (packages right) ]) ;
|
OpamPackage.Set.cardinal right, [ H.code ~a:[H.a_class ["code-diff"]] (packages right) ]) ;
|
||||||
("opam-packages-version-diff", "Opam packages with version changes",
|
("opam-packages-version-diff", "Opam packages with version changes",
|
||||||
List.length version_diff, [ H.code (package_diffs version_diff) ]) ;
|
List.length version_diff, [ H.code ~a:[H.a_class ["code-diff"]] (package_diffs version_diff) ]) ;
|
||||||
] @ (match duniverse with
|
] @ (match duniverse with
|
||||||
| Ok (duniverse_left, duniverse_right, duniverse_content_diff) ->
|
| Ok (duniverse_left, duniverse_right, duniverse_content_diff) ->
|
||||||
[
|
[
|
||||||
("duniverse-dirs-removed", "Duniverse directories removed",
|
("duniverse-dirs-removed", "Duniverse directories removed",
|
||||||
List.length duniverse_left, [ H.code (duniverse_dirs duniverse_left) ]) ;
|
List.length duniverse_left, [ H.code ~a:[H.a_class ["code-diff"]] (duniverse_dirs duniverse_left) ]) ;
|
||||||
("duniverse-dirs-installed", "New duniverse directories installed",
|
("duniverse-dirs-installed", "New duniverse directories installed",
|
||||||
List.length duniverse_right, [ H.code (duniverse_dirs duniverse_right) ]) ;
|
List.length duniverse_right, [ H.code ~a:[H.a_class ["code-diff"]] (duniverse_dirs duniverse_right) ]) ;
|
||||||
("duniverse-dirs-content-diff", "Duniverse directories with content changes",
|
("duniverse-dirs-content-diff", "Duniverse directories with content changes",
|
||||||
List.length duniverse_content_diff, [ H.code (duniverse_diffs duniverse_content_diff) ]) ;
|
List.length duniverse_content_diff, [ H.code ~a:[H.a_class ["code-diff"]] (duniverse_diffs duniverse_content_diff) ]) ;
|
||||||
]
|
]
|
||||||
| Error `Msg msg -> [ "duniverse-dirs-error", "Duniverse parsing error", 1, [ H.txt msg ] ]
|
| Error `Msg msg -> [ "duniverse-dirs-error", "Duniverse parsing error", 1, [ H.txt msg ] ]
|
||||||
) @ [
|
) @ [
|
||||||
("opam-packages-opam-diff", "Opam packages with changes in their opam file",
|
("opam-packages-opam-diff", "Opam packages with changes in their opam file",
|
||||||
List.length opam_diff, opam_diffs opam_diff) ;
|
List.length opam_diff, opam_diffs opam_diff) ;
|
||||||
("env-removed", "Environment variables removed",
|
("env-removed", "Environment variables removed",
|
||||||
List.length removed_env, [ H.code (key_values removed_env) ]) ;
|
List.length removed_env, [ H.code ~a:[H.a_class ["code-diff"]] (key_values removed_env) ]) ;
|
||||||
("env-added", "New environment variables added",
|
("env-added", "New environment variables added",
|
||||||
List.length added_env, [ H.code (key_values added_env) ]) ;
|
List.length added_env, [ H.code ~a:[H.a_class ["code-diff"]] (key_values added_env) ]) ;
|
||||||
("env-changed", "Environment variables changed",
|
("env-changed", "Environment variables changed",
|
||||||
List.length changed_env, [ H.code (key_value_changes changed_env) ]) ;
|
List.length changed_env, [ H.code ~a:[H.a_class ["code-diff"]] (key_value_changes changed_env) ]) ;
|
||||||
("pkgs-removed", "System packages removed",
|
("pkgs-removed", "System packages removed",
|
||||||
List.length removed_pkgs, [ H.code (key_values removed_pkgs) ]) ;
|
List.length removed_pkgs, [ H.code ~a:[H.a_class ["code-diff"]] (key_values removed_pkgs) ]) ;
|
||||||
("pkgs-added", "New system packages added",
|
("pkgs-added", "New system packages added",
|
||||||
List.length added_pkgs, [ H.code (key_values added_pkgs) ]) ;
|
List.length added_pkgs, [ H.code ~a:[H.a_class ["code-diff"]] (key_values added_pkgs) ]) ;
|
||||||
("pkgs-changed", "System packages changed",
|
("pkgs-changed", "System packages changed",
|
||||||
List.length changed_pkgs, [ H.code (key_value_changes changed_pkgs) ]) ;
|
List.length changed_pkgs, [ H.code ~a:[H.a_class ["code-diff"]] (key_value_changes changed_pkgs) ]) ;
|
||||||
])
|
])
|
||||||
in
|
in
|
||||||
layout
|
layout
|
||||||
|
@ -928,31 +996,31 @@ let compare_builds
|
||||||
~title:(Fmt.str "Comparing builds %a and %a"
|
~title:(Fmt.str "Comparing builds %a and %a"
|
||||||
Uuidm.pp build_left.uuid Uuidm.pp build_right.uuid)
|
Uuidm.pp build_left.uuid Uuidm.pp build_right.uuid)
|
||||||
([
|
([
|
||||||
H.h1 [H.txt "Comparing builds"];
|
H.h1 ~a:[H.a_class ["text-center"]] [H.txt "Comparing builds"];
|
||||||
H.h2 [
|
H.h2 ~a:[H.a_class ["text-center"]] [
|
||||||
H.txt "Builds ";
|
H.txt "Builds ";
|
||||||
H.a ~a:H.[ a_href @@
|
(H.a ~a:[ H.a_href @@
|
||||||
Link.Job_build.make
|
Link.Job_build.make
|
||||||
~job_name:job_left
|
~job_name:job_left
|
||||||
~build:build_left.uuid () ]
|
~build:build_left.uuid (); H.a_class ["link"] ]
|
||||||
[ txtf "%s@%a %a"
|
[ txtf "%s@%a %a"
|
||||||
job_left
|
job_left
|
||||||
pp_ptime build_left.start
|
pp_ptime build_left.start
|
||||||
pp_platform (Some build_left.platform)];
|
pp_platform (Some build_left.platform)]);
|
||||||
H.txt " and ";
|
H.txt " and ";
|
||||||
H.a ~a:H.[ a_href @@
|
H.a ~a:[ H.a_href @@
|
||||||
Link.Job_build.make
|
Link.Job_build.make
|
||||||
~job_name:job_right
|
~job_name:job_right
|
||||||
~build:build_right.uuid () ]
|
~build:build_right.uuid (); H.a_class ["link"] ]
|
||||||
[ txtf "%s@%a %a"
|
[ txtf "%s@%a %a"
|
||||||
job_right
|
job_right
|
||||||
pp_ptime build_right.start
|
pp_ptime build_right.start
|
||||||
pp_platform (Some build_right.platform)];
|
pp_platform (Some build_right.platform)];
|
||||||
];
|
];
|
||||||
H.h3 [ H.a ~a:H.[
|
H.h3 ~a:[H.a_class ["text-right"]] [ H.a ~a:[
|
||||||
a_href @@ Link.Compare_builds.make
|
H.a_href @@ Link.Compare_builds.make
|
||||||
~left:build_right.uuid
|
~left:build_right.uuid
|
||||||
~right:build_left.uuid () ]
|
~right:build_left.uuid (); H.a_class ["link"] ]
|
||||||
[H.txt "Compare in reverse direction"]] ;
|
[H.txt "Compare in reverse direction"]] ;
|
||||||
H.ul (List.rev items) ] @ data)
|
H.ul (List.rev items) ] @ data)
|
||||||
|
|
||||||
|
|
|
@ -4,27 +4,30 @@
|
||||||
remote_instance=https://builds.robur.coop
|
remote_instance=https://builds.robur.coop
|
||||||
local_user_pass=test:test
|
local_user_pass=test:test
|
||||||
local_instance="http://${local_user_pass}@localhost:3000"
|
local_instance="http://${local_user_pass}@localhost:3000"
|
||||||
limit=100
|
global_limit=100
|
||||||
|
per_job_limit=3 # Limit to 3 builds per job
|
||||||
|
|
||||||
curl_json () {
|
curl_json () {
|
||||||
curl --silent --fail --location --header "Accept: application/json" "$@"
|
curl --silent --fail --location --header "Accept: application/json" "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_json "${remote_instance}/" | jq -r .jobs[] | {
|
curl_json "${remote_instance}/" | jq -r .jobs[] | {
|
||||||
while read -r job_name; do
|
while read -r job_name; do
|
||||||
curl_json "${remote_instance}/job/${job_name}" | jq -r .builds[].uuid | {
|
build_count=0
|
||||||
while read -r build_uuid; do
|
curl_json "${remote_instance}/job/${job_name}" | jq -r .builds[].uuid | {
|
||||||
if [ "$limit" -eq 0 ]; then
|
while read -r build_uuid; do
|
||||||
break 2;
|
if [ "$global_limit" -eq 0 ] || [ "$build_count" -ge "$per_job_limit" ]; then
|
||||||
fi
|
break 2
|
||||||
dest=$(mktemp "builder-${build_uuid}.XXXXXXXXXX")
|
fi
|
||||||
curl --silent --fail "${remote_instance}/job/${job_name}/build/${build_uuid}/exec" > "$dest" && {
|
dest=$(mktemp "builder-${build_uuid}.XXXXXXXXXX")
|
||||||
echo "Uploading $job_name build $build_uuid"
|
curl --silent --fail "${remote_instance}/job/${job_name}/build/${build_uuid}/exec" > "$dest" && {
|
||||||
curl --data-binary "@${dest}" "${local_instance}/upload"
|
echo "Uploading $job_name build $build_uuid"
|
||||||
}
|
curl --data-binary "@${dest}" "${local_instance}/upload"
|
||||||
rm -f "$dest"
|
}
|
||||||
limit=$((limit - 1))
|
rm -f "$dest"
|
||||||
done
|
global_limit=$((global_limit - 1))
|
||||||
}
|
build_count=$((build_count + 1))
|
||||||
done
|
done
|
||||||
|
}
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue
if you're not going to use these why keep them?
Yes I'll do some code cleaning to remove the things we aren't using