Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • loqs/rebuilderd-website
  • iyanmv/rebuilderd-website
  • mgolebiowski/rebuilderd-website
  • xexaxo/rebuilderd-website
  • duthils/rebuilderd-website
  • aditya/rebuilderd-website
  • kpcyrd/rebuilderd-website
  • archlinux/rebuilderd-website
8 results
Show changes
Commits on Source (54)
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
charset = utf-8
indent_style = tab
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
; Python: PEP8 defines 4 spaces for indentation
[*.py]
indent_style = space
indent_size = 4
; YAML format, 2 spaces
[*.yaml, *.yml]
indent_style = space
indent_size = 2
; HTML, CSS and JavaScript, 4 spaces
[*.{html,css,js}]
charset = utf-8
indent_size = 2
indent_style = space
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
Subproject commit 5d3ec1f7a122283fbd5aebb6c8a9c87f43b18ac4
......@@ -2,9 +2,15 @@ image: "archlinux:latest"
dist:
script:
- pacman -Syu --needed --noconfirm yarn ruby-sass make
- pacman -Syu --needed --noconfirm yarn ruby-sass make git svgcleaner
- make
- make dist
after_script:
- echo "dist_size_bytes $(make distsize) " > metrics.txt
artifacts:
reports:
metrics: metrics.txt
dotenv: build.env
include:
- template: Dependency-Scanning.gitlab-ci.yml
[submodule ".external/archlinux-common-style"]
path = .external/archlinux-common-style
url = https://gitlab.archlinux.org/archlinux/archlinux-common-style
localhost:8881 {
errors stderr
log stdout
http://localhost:8881
gzip
log
proxy /api https://reproducible.archlinux.org/ {
transparent
}
encode gzip
proxy / 127.0.0.1:9966 {
transparent
}
proxy /livereload 127.0.0.1:9966 {
websocket
transparent
}
}
reverse_proxy localhost:9966
reverse_proxy /api/* https://reproducible.archlinux.org
reverse_proxy /livereload localhost:9966
......@@ -11,6 +11,7 @@ PORT ?= 9966
HOST ?= localhost
VERSION ?= $(shell git describe --tags --always --dirty --match=v* 2>/dev/null | sed 's/^v//' || \
cat $(CURDIR)/.version 2> /dev/null || echo 0.0.0-unreleased)
ARCHLOGO = archlogo.8a05bc7f6cd1.svg
all: vendor
......@@ -32,22 +33,38 @@ js-watcher: vendor
dist: vendor
@mkdir -p "dist/${PACKAGE_NAME}-${VERSION}"
cp -avf public/index.html "dist/${PACKAGE_NAME}-${VERSION}/index.html"
# TODO: cache-invalidation with version string replaced in html file
cp -avf public/favicon.ico "dist/${PACKAGE_NAME}-${VERSION}/favicon.ico"
$(SASS) -t compressed src/style.scss "dist/${PACKAGE_NAME}-${VERSION}/bundle.css"
$(YARN) run -s browserify -t babelify src/index.js | $(YARN) run -s uglifyjs > "dist/${PACKAGE_NAME}-${VERSION}/bundle.js"
svgcleaner public/${ARCHLOGO} "dist/${PACKAGE_NAME}-${VERSION}/archlogo-${VERSION}.svg"
cp -vf public/favicon.ico -t "dist/${PACKAGE_NAME}-${VERSION}/"
$(SASS) -t compressed src/style.scss "dist/${PACKAGE_NAME}-${VERSION}/bundle-${VERSION}.css"
$(YARN) run -s browserify -t babelify src/index.js | $(YARN) run -s terser --compress --mangle > "dist/${PACKAGE_NAME}-${VERSION}/bundle-${VERSION}.js"
# sed the version file in html
@sed -i 's/bundle.js/bundle-${VERSION}.js/' "dist/${PACKAGE_NAME}-${VERSION}/index.html"
@sed -i 's/bundle.css/bundle-${VERSION}.css/' "dist/${PACKAGE_NAME}-${VERSION}/index.html"
# sed the svg version in css
@sed -i 's/${ARCHLOGO}/archlogo-${VERSION}.svg/' "dist/${PACKAGE_NAME}-${VERSION}/bundle-${VERSION}.css"
cd dist && tar --owner=0 --group=0 -czvf ${PACKAGE_NAME}-${VERSION}.tar.gz "${PACKAGE_NAME}-${VERSION}"
# Yarn
.PHONY: vendor
vendor: .yarninstall
vendor: submodule .yarninstall
.yarninstall: package.json
@$(YARN) install --silent
@touch $@
.PHONY:
submodule:
git submodule update --init --recursive
make -C .external/archlinux-common-style
.PHONY:
distsize:
@du -s "dist/${PACKAGE_NAME}-${VERSION}"
.PHONY:
clean:
......
../.external/archlinux-common-style/img/archlogo.8a05bc7f6cd1.svg
\ No newline at end of file
public/favicon.ico

575 B

../.external/archlinux-common-style/img/favicon.ico
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
role="img"
viewBox="-3.29 29.96 14.840857 12.000001"
version="1.1"
id="svg4"
sodipodi:docname="in-toto.svg"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)"
width="14.840857"
height="12"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="9.6242015"
inkscape:cx="25.404705"
inkscape:cy="14.702518"
inkscape:window-width="1916"
inkscape:window-height="1060"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="svg4"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<path
d="m 11.405437,32.497306 c 0,0 -0.297326,-0.866189 -0.731522,-1.33095 -0.314258,-0.33638 -0.657577,-0.628783 -1.0630262,-0.830542 l 0.0043,-6.67e-4 c -0.01214,-0.0052 -0.02428,-0.01018 -0.03642,-0.01523 -0.03351,-0.01608 -0.06736,-0.0317 -0.101874,-0.04639 -1.131349,-0.481191 -2.232577,-0.411572 -3.28832,0.230139 a 2.8599687,2.8599687 0 0 0 -0.341432,0.248664 c -0.319852,0.266635 -1.839074,1.3446 -1.584916,1.874201 0.124121,0.258551 0.813788,0.893584 1.100126,0.892634 0.29289,-10e-4 1.473795,-1.091544 1.815452,-1.262144 0.698821,-0.34886 1.524004,-0.35334 2.005717,0.251119 0.498043,0.624951 0.418386,1.525341 -0.187393,2.031578 -0.827694,0.691756 -1.630056,1.415984 -2.49084,2.066138 -0.331225,0.250176 -0.659327,0.356276 -0.969076,0.353337 0.667567,-0.222733 0.616473,-1.513664 0.209407,-2.22336 -0.535035,-0.932826 -1.329962,-1.513103 -2.385504,-1.714666 -1.062228,-0.20284 -2.020517,0.04871 -2.858947,0.729792 -0.721415,0.585936 -1.474016,1.137567 -2.149673,1.775843 -0.426796,0.403129 -0.830473,0.83627 -1.103884,1.314123 -0.487606,0.85241 -0.703808,1.565703 -0.39223,2.58177 0,0 0.38128,0.93633 0.770326,1.330947 0.358218,0.36326 0.690998,0.694082 1.158245,0.892832 1.131344,0.481191 2.232588,0.411569 3.288316,-0.230139 a 2.8599974,2.8599974 0 0 0 0.341436,-0.248666 c 0.319852,-0.266636 1.839074,-1.344596 1.584911,-1.874199 -0.124123,-0.25855 -0.813788,-0.893586 -1.100126,-0.892637 -0.292885,0.001 -1.47379,1.091547 -1.815441,1.262147 -0.698822,0.348854 -1.523994,0.353338 -2.005712,-0.251125 -0.498053,-0.624949 -0.41838,-1.525342 0.187382,-2.031575 0.827699,-0.691761 1.630057,-1.415987 2.490843,-2.066144 0.417644,-0.315438 0.830307,-0.401909 1.207481,-0.329347 a 1.8863898,1.8863898 0 0 0 -0.309482,2.260997 c 0.534968,0.932826 1.287606,1.467845 2.343133,1.669441 l 0.0652,0.0107 c 0.998134,0.148732 1.901994,-0.110446 2.697791,-0.7569 0.721419,-0.585936 1.474027,-1.137567 2.149683,-1.775842 0.4267902,-0.403126 0.8304732,-0.83627 1.1038832,-1.314124 0.487604,-0.852404 0.703802,-1.565701 0.39222,-2.581767 z"
id="path2"
style="stroke-width:0.0421598" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0114.25 14H1.75A1.75 1.75 0 010 12.25v-8.5zm1.75-.25a.25.25 0 00-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25v-8.5a.25.25 0 00-.25-.25H1.75zM3.5 6.25a.75.75 0 01.75-.75h7a.75.75 0 010 1.5h-7a.75.75 0 01-.75-.75zm.75 2.25a.75.75 0 000 1.5h4a.75.75 0 000-1.5h-4z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M11.5 7a4.499 4.499 0 11-8.998 0A4.499 4.499 0 0111.5 7zm-.82 4.74a6 6 0 111.06-1.06l3.04 3.04a.75.75 0 11-1.06 1.06l-3.04-3.04z"/></svg>
\ No newline at end of file
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
......@@ -12,6 +12,11 @@
<div id="root"></div>
<footer class="footer">
<div class="content has-text-centered">
<noscript>
<h1 class="title">Please enable javascript to use this site</h1>
<p>You may also use the api with rebuildctl from the commandline.</p>
<br>
</noscript>
<p>
<strong>Arch Linux</strong> Reproducible Status by <a href="https://github.com/jelly">Jelle van der Waa</a>. The source code is licensed
<a href="http://opensource.org/licenses/mit-license.php">MIT</a> and available <a href="https://gitlab.archlinux.org/archlinux/rebuilderd-website">here</a>.
......
......@@ -8,19 +8,17 @@ tmux new-session -d -s ${SESSION}
# Setup panes
tmux new-window -t ${SESSION}:0 -n "${SESSION}"
tmux split-window -v
tmux select-pane -t 0
tmux send-keys "make js-watcher" C-m
tmux split-window -v
tmux select-pane -t 1
tmux send-keys "make sass-watcher" C-m
tmux split-window -v
tmux select-pane -t 2
tmux send-keys "caddy" C-m
tmux send-keys "caddy run" C-m
# Set default window
tmux select-window -t $SESSION:1
tmux select-window -t $SESSION:+1
# Attach to session
tmux -2 attach-session -t $SESSION
tmux attach-session -t $SESSION
......@@ -11,15 +11,16 @@ class App extends React.Component {
super(props);
this.state = {
fetchFailed: false,
suites: []
suites: [],
dashboard: null,
};
}
render() {
const { fetchFailed, suites } = this.state;
const { fetchFailed, dashboard, suites } = this.state;
return (
<React.Fragment>
<Header fetchFailed={fetchFailed} suites={suites}/>
<Header fetchFailed={fetchFailed} dashboard={dashboard}/>
<Body fetchFailed={fetchFailed} suites={suites}/>
</React.Fragment>
);
......@@ -39,7 +40,24 @@ class App extends React.Component {
}
}
componentDidMount() {
loadDashboard() {
const url = '/api/v0/dashboard';
fetch(url).then((response) => {
if (!response.ok) {
this.setState({fetchFailed: true});
throw new Error(response.statusText);
}
return response.json();
}).then((data) => {
this.setState({dashboard: data});
}).catch((error) => {
console.error(error);
this.setState({fetchFailed: true});
});
}
loadPkgs() {
const url = '/api/v0/pkgs/list';
fetch(url).then((response) => {
......@@ -69,10 +87,17 @@ class App extends React.Component {
this.setState({suites: suiteList});
}).catch((error) => {
console.log(error);
console.error(error);
this.setState({fetchFailed: true});
});
}
componentDidMount() {
this.loadDashboard()
this.loadPkgs()
}
}
module.exports = {App};
// vim: ts=2 sw=2 et:
......@@ -11,6 +11,11 @@ class Body extends React.Component {
return (
<React.Fragment>
{!fetchFailed && !suites.length &&
<section className="section">
<p><b>Loading packages...</b></p>
</section>
}
{ fetchFailed &&
<section className="section">
<div className="tile box has-background-danger">
......
'use strict';
const React = require('react');
import ArchLinuxNavbar from './navbar';
class Header extends React.Component {
calculateSuiteStats(data) {
let good = 0;
let bad = 0;
let unknown = 0;
for (let pkg of data) {
switch (pkg.status) {
case 'GOOD':
good++;
break
case 'BAD':
bad++;
break
case 'UNKWN':
unknown++;
break
}
}
let good = data['good'];
let bad = data['bad'];
let unknown = data['unknown'];
const percentage = (good / data.length * 100).toFixed(1);
const percentage = (good / (good + bad + unknown) * 100).toFixed(1);
return {good, bad, unknown, percentage};
}
// TODO: this is duplciated code from App.js
compareSuites(a, b) {
if (a.name == 'core') {
return -1;
} else if (a.name == 'core' && b.name != 'core') {
return -1;
} else if (a.name == 'extra' && b.name == 'core') {
return 1;
} else if (a.name == 'extra' && b.name != 'core') {
return -1;
} else {
return 1;
}
}
render() {
const {fetchFailed, suites } = this.props;
const {fetchFailed, dashboard, suites } = this.props;
const overall = {good: 0, unknown: 0, bad: 0};
const suitesStats = [];
for (let suite of suites) {
const {good, bad, unknown, percentage} = this.calculateSuiteStats(suite.pkgs);
suitesStats.push({name: suite.name, good, bad, unknown, percentage});
if (dashboard) {
for (const [key, value] of Object.entries(dashboard.suites)) {
const {good, bad, unknown, percentage} = this.calculateSuiteStats(value);
overall['good'] += good;
overall['bad'] += bad;
overall['unknown'] += unknown;
suitesStats.push({name: key, good, bad, unknown, percentage});
}
suitesStats.sort(this.compareSuites);
}
const {good, bad, unknown, percentage} = this.calculateSuiteStats(overall);
const overallStats = {name: 'overall', good, bad, unknown, percentage};
return (
<section className="hero is-primary">
<ArchLinuxNavbar />
<div className="hero-body">
<div id="status" className="container">
<h1 className="title">Arch Linux Reproducible status</h1>
<p>Welcome to the official experimental Arch Linux <a href="https://github.com/kpcyrd/rebuilderd">rebuilderd</a> instance, this page shows the results of verification builds of official Arch Linux packages in the repositories in an effort to be fully reproducible. For more information read the <a href="https://reproducible-builds.org/">Reproducible Builds website</a> or join the <a href="ircs://chat.freenode.net/archlinux-reproducible">#archlinux-reproducible</a> IRC channel on <a href="https://freenode.net/">Freenode</a>.</p>
<br/>
{!fetchFailed && suitesStats.map(function(repo, index) {
return <p key={ index }><a href={"#" + repo.name }>[{ repo.name }]</a> repository is { repo.percentage }% reproducible with { repo.bad } bad and { repo.unknown } unknown packages.</p>;
})}
<div id="status">
<h1 className="title">Reproducible status</h1>
<p>Welcome to the official experimental Arch Linux <a href="https://github.com/kpcyrd/rebuilderd">rebuilderd</a> instance, this page shows the results of verification builds of official Arch Linux packages in the repositories in an effort to be fully reproducible.</p>
<p>For more information read the <a href="https://reproducible-builds.org/">Reproducible Builds website</a> or join the <a href="ircs://irc.libera.chat/archlinux-reproducible">#archlinux-reproducible</a> IRC channel on <a href="https://libera.chat/">Libera Chat</a>.</p>
<br/>
<ul className="repo-summary">
{!fetchFailed && !dashboard &&
<p><b>Loading stats...</b></p>
}
{!fetchFailed && dashboard &&
<li key="overall">Arch Linux is <span className="has-text-weight-bold">{ overallStats.percentage }%</span> reproducible with <span className="bad has-text-weight-bold">{ overallStats.bad } bad</span> <span className="unknown has-text-weight-bold">{ overallStats.unknown } unknown</span> and <span className="good has-text-weight-bold">{ overallStats.good } good</span> packages.</li>
}
{!fetchFailed && suitesStats.map(function(repo, index) {
return <li key={ index }><a href={"#" + repo.name }>[{ repo.name }]</a> repository is <span className="has-text-weight-bold">{ repo.percentage }%</span> reproducible with <span className="bad has-text-weight-bold">{ repo.bad } bad</span> <span className="unknown has-text-weight-bold">{ repo.unknown } unknown</span> and <span className="good has-text-weight-bold">{ repo.good } good</span> packages.</li>;
})}
</ul>
</div>
</div>
</section>
......@@ -54,3 +76,5 @@ class Header extends React.Component {
}
module.exports = {Header};
// vim: ts=2 sw=2 et:
'use strict';
const React = require('react');
import Collapsible from 'react-collapsible'
function StatusSection(props) {
const isOpen = props.open || false;
const content = (
<ul>
{props.pkgs.map(function(pkg) {
const url=`https://www.archlinux.org/packages/${pkg.suite}/${pkg.architecture}/${pkg.name}`;
let links='';
if (pkg.build_id) {
const build_log_url=`/api/v0/builds/${pkg.build_id}/log`;
const build_log_link=<a href={build_log_url} target="_blank noreferrer" title="build log"><img src="icons/note-16.svg" className="icon" /></a>;
let diffoscope_link='';
let attestation_link='';
if (pkg.has_diffoscope) {
const diffoscope_url=`/api/v0/builds/${pkg.build_id}/diffoscope`;
diffoscope_link=<a href={diffoscope_url} target="_blank noreferrer" title="diffoscope"><img src="icons/search-16.svg" className="icon" /></a>;
}
if (pkg.has_attestation) {
const attestation_url=`/api/v0/builds/${pkg.build_id}/attestation`;
attestation_link=<a href={attestation_url} target="_blank noreferrer" title="attestation"><img src="icons/in-toto.svg" className="icon" /></a>;
}
links=<span className="noselect"> {build_log_link} {diffoscope_link} {attestation_link}</span>;
}
return <li key={pkg.name}><p className="subtitle is-6"><a href={url} target="_blank noreferrer" >{pkg.name} {pkg.version}</a>{links}</p></li>
})}
</ul>
);
const label = `${props.label} (${props.pkgs.length})`;
return (
<div className={ props.label }>
<Collapsible trigger={label} lazyRender open={isOpen}>{ content }</Collapsible>
</div>
);
}
class Section extends React.Component {
render() {
const { suite } = this.props;
const good = suite.pkgs.filter(pkg => pkg.status == "GOOD");
const bad = suite.pkgs.filter(pkg => pkg.status == "BAD");
const unknown = suite.pkgs.filter(pkg => pkg.status == "UNKWN");
const name = `${suite.name} (${suite.pkgs.length})`;
return (
<section key={suite.name} className="section pt-4 pb-4" >
<div className="tile box has-background-danger">
<div className="content">
<p className='title is-5 has-text-white'>{ suite.name }</p>
<ul>
{suite.pkgs.map(function(pkg) {
if (pkg.status == 'BAD') {
return <li key={pkg.name}><p className="subtitle is-6 has-text-white">{pkg.name}-{pkg.version}</p></li>
}
})}
</ul>
</div>
<section key={suite.name} className="section pt-4 pb-4" id={ suite.name }>
<div className="tile box has-background-info">
<Collapsible trigger={ name } open>
{good.length > 0 && <StatusSection label="good" pkgs={ good } />}
{bad.length > 0 && <StatusSection label="bad" pkgs={ bad } open />}
{unknown.length > 0 && <StatusSection label="unknown" pkgs={ unknown } open />}
</Collapsible>
</div>
</section>
)
......
'use strict';
const React = require('react');
const ReactDOM = require('react-dom');
import { createRoot } from 'react-dom/client';
const { App } = require('./App');
ReactDOM.render(<App />, document.getElementById('root'));
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<App tab="home" />);