General improvements.
This commit is contained in:
232
git-portfolio
232
git-portfolio
@@ -1,23 +1,171 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
function cleanup() {
|
||||||
|
rm -rf repos tmp-portfolio
|
||||||
|
}
|
||||||
|
directory="$1"
|
||||||
|
if [ ! "$directory" ]; then
|
||||||
|
echo "Usage: $0 <directory>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
trap cleanup EXIT
|
||||||
GIT_URL='https://git.seanhealy.ie'
|
GIT_URL='https://git.seanhealy.ie'
|
||||||
GIT_USER=sean
|
GIT_USER=sean
|
||||||
|
GITHUB_USER=seanhly
|
||||||
|
GITLAB_USER=seanhly
|
||||||
|
CODEBERG_USER=seanhly
|
||||||
TOKEN="$(pass gitea-api-token)"
|
TOKEN="$(pass gitea-api-token)"
|
||||||
EXCLUDE_LANGUAGES=(Makefile TeX)
|
EXCLUDE_LANGUAGES=(Makefile TeX)
|
||||||
|
function my_curl() {
|
||||||
|
curl -s "$GIT_URL/api/v1/users/$GIT_USER/repos" \
|
||||||
|
-H 'Accept: application/json' \
|
||||||
|
-H "Authorization: $TOKEN" | jq -c '
|
||||||
|
sort_by(.updated_at) | reverse | .[] | select(.private == false) | select(.empty == false) | [
|
||||||
|
.name,
|
||||||
|
.description,
|
||||||
|
.languages_url,
|
||||||
|
.website,
|
||||||
|
.updated_at,
|
||||||
|
.avatar_url,
|
||||||
|
.licenses
|
||||||
|
]'
|
||||||
|
}
|
||||||
|
mkdir -p tmp-portfolio
|
||||||
|
portfolio_index=""
|
||||||
|
while read project; do
|
||||||
|
i=0
|
||||||
|
name=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
||||||
|
echo "Processing $name..."
|
||||||
|
description=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
||||||
|
languages_url=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
||||||
|
languages="$(curl -s "$languages_url" -H "Authorization: $TOKEN" |
|
||||||
|
jq -r 'to_entries[]|[.key,.value]|@tsv')"
|
||||||
|
total_bytes=$(echo "$languages" | awk '{sum += $2} END {print sum}')
|
||||||
|
languages_percentages=$(echo "$languages" |
|
||||||
|
awk -v total="$total_bytes" '{print $2 / total"\t"$1}' |
|
||||||
|
sort -n -r
|
||||||
|
)
|
||||||
|
git clone --depth 1 -b main --single-branch "$GIT_URL/$GIT_USER/$name" "repos/$name" 2>/dev/null >/dev/null
|
||||||
|
echo $'Percentage\tLanguages' > /tmp/languages.tsv
|
||||||
|
echo "$languages_percentages" >> /tmp/languages.tsv
|
||||||
|
(cat > /tmp/plot.R) <<EOF
|
||||||
|
#!/usr/bin/Rscript
|
||||||
|
library(ggplot2)
|
||||||
|
data <- read.csv("/tmp/languages.tsv", sep="\t")
|
||||||
|
data\$Languages <- factor(data\$Languages, levels=data\$Languages[order(data\$Percentage, decreasing=TRUE)])
|
||||||
|
if (nrow(data) == 1) {
|
||||||
|
data\$Percentage <- 0
|
||||||
|
}
|
||||||
|
plot <- ggplot(data, aes(x="", y=Percentage, fill=Languages)) +
|
||||||
|
geom_bar(stat="identity", width=1) +
|
||||||
|
coord_polar("y", start=0) +
|
||||||
|
theme_void() +
|
||||||
|
scale_fill_brewer(palette="Set3") +
|
||||||
|
theme(legend.text=element_text(size=16), legend.title=element_text(size=18))
|
||||||
|
ggsave("repos/$name/$name-languages.png", plot, width=5, height=5)
|
||||||
|
EOF
|
||||||
|
Rscript /tmp/plot.R
|
||||||
|
language_list="$(
|
||||||
|
(cut -d$'\t' -f2 <<< "$languages_percentages") |
|
||||||
|
grep -v -F -w -f <(printf "%s\n" "${EXCLUDE_LANGUAGES[@]}")
|
||||||
|
)"
|
||||||
|
website=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
||||||
|
updated_at=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
||||||
|
updated_at=$(date -d "$updated_at" +"%B %d, %Y")
|
||||||
|
avatar_url=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
||||||
|
licenses=$(jq -r '.['$i'] | join(", ")' <<< "$project"); i=$((i + 1))
|
||||||
|
mkdir -p repos
|
||||||
|
cp project-style.css "repos/$name/"
|
||||||
|
(cd "repos/$name" && (cat > "$name.qmd") <<EOF
|
||||||
|
---
|
||||||
|
execute:
|
||||||
|
enabled: false
|
||||||
|
format:
|
||||||
|
html:
|
||||||
|
embed-resources: true
|
||||||
|
css: project-style.css
|
||||||
|
---
|
||||||
|
|
||||||
|
[← Back to Portfolio](index.html)
|
||||||
|
|
||||||
|
Mirrors:
|
||||||
|
|
||||||
|
- [codeberg.org/$CODEBERG_USER/$name](https://codeberg.org/$CODEBERG_USER/$name)
|
||||||
|
- [github.com/$GITHUB_USER/$name](https://github.com/$GITHUB_USER/$name)
|
||||||
|
- [gitlab.com/$GITLAB_USER/$name](https://gitlab.com/$GITLAB_USER/$name)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
\`\`\`{bash}
|
||||||
|
git clone https://codeberg.org/$CODEBERG_USER/$name
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
$(cat README.md)
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
portfolio_index+="$(
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
|
<a href="$name.html">
|
||||||
|
<section>
|
||||||
|
<h2>$name</h2>
|
||||||
|
<img src="$avatar_url" alt="Avatar" width="100" height="100" alt="$name (logo)">
|
||||||
|
<p>$description</p>
|
||||||
|
<ul class="languages">
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
(cd "repos/$name" &&
|
||||||
|
quarto render "$name.qmd" -o "$name.html" >/dev/null 2>/dev/null &&
|
||||||
|
mv "$name.html" "../../tmp-portfolio/$name.html"
|
||||||
|
)
|
||||||
|
while read -r language; do
|
||||||
|
portfolio_index+="<li class='language'>$language</li>"
|
||||||
|
done <<< "$language_list"
|
||||||
|
portfolio_index+="$(
|
||||||
|
cat <<EOF
|
||||||
|
</ul>
|
||||||
|
<p>Last Updated: $updated_at</p>
|
||||||
|
<p>Licenses: $licenses</p>
|
||||||
|
</section>
|
||||||
|
</a>
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
done < <(my_curl)
|
||||||
|
portfolio_index="$(
|
||||||
|
cat <<EOF
|
||||||
|
<h1>Projects</h1>
|
||||||
<div id="git-portfolio">
|
<div id="git-portfolio">
|
||||||
<style>
|
<style>
|
||||||
#git-portfolio {
|
#git-portfolio {
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
#git-portfolio section {
|
#git-portfolio section {
|
||||||
background: wheat;
|
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
max-width: 400px;
|
|
||||||
border: 2px black solid;
|
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
|
background: linear-gradient(30deg, rgba(100, 43, 200, 0.4), rgba(198, 66, 110, 0.4));
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 420px;
|
||||||
|
}
|
||||||
|
#git-portfolio a {
|
||||||
|
color: black;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
min-width: 400px;
|
||||||
|
max-width: 450px;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
#git-portfolio section ul.languages {
|
||||||
|
float: right;
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
#git-portfolio h2 {
|
||||||
|
font-family: "Courier New", monospace;
|
||||||
}
|
}
|
||||||
li.language {
|
li.language {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -27,63 +175,19 @@ li.language {
|
|||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<h1>Projects</h1>
|
$portfolio_index
|
||||||
|
</div>
|
||||||
EOF
|
EOF
|
||||||
curl "$GIT_URL/api/v1/users/$GIT_USER/repos" \
|
)"
|
||||||
-H 'Accept: application/json' \
|
echo "$portfolio_index" > tmp-portfolio/index.html
|
||||||
-H "Authorization: $TOKEN" | jq -c '
|
if [ -d "$directory" ]; then
|
||||||
sort_by(.updated_at) | reverse | .[] | select(.private == false) | select(.empty == false) | [
|
if [ "$(du -sb "$directory" | cut -f1)" -gt $((1024 * 1024 * 400)) ]; then
|
||||||
.name,
|
echo "Error: Directory '$directory' contains more than 400MB of data. Exiting to prevent data loss."
|
||||||
.description,
|
exit 1
|
||||||
.languages_url,
|
fi
|
||||||
.html_url,
|
else
|
||||||
.clone_url,
|
mkdir -p "$directory"
|
||||||
.website,
|
fi
|
||||||
.updated_at,
|
rsync -a "tmp-portfolio/" "$directory/"
|
||||||
.avatar_url,
|
rm -rf repos
|
||||||
.licenses
|
rm -r tmp-portfolio
|
||||||
]' |
|
|
||||||
while read project; do
|
|
||||||
i=0
|
|
||||||
name=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
|
||||||
description=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
|
||||||
languages_url=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
|
||||||
languages="$(curl -s "$languages_url" -H "Authorization: $TOKEN" |
|
|
||||||
jq -r 'to_entries[]|[.key,.value]|@tsv')"
|
|
||||||
total_bytes=$(echo "$languages" | awk '{sum += $2} END {print sum}')
|
|
||||||
languages_percentages=$(echo "$languages" |
|
|
||||||
awk -v total="$total_bytes" '{print $2, $1}' |
|
|
||||||
sort -n -r |
|
|
||||||
cut -d' ' -f2 | grep -v -F -w -f <(printf "%s\n" "${EXCLUDE_LANGUAGES[@]}")
|
|
||||||
)
|
|
||||||
html_url=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
|
||||||
clone_url=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
|
||||||
website=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
|
||||||
updated_at=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
|
||||||
updated_at=$(date -d "$updated_at" +"%B %d, %Y")
|
|
||||||
avatar_url=$(jq -r ".[$i]" <<< "$project"); i=$((i + 1))
|
|
||||||
licenses=$(jq -r '.['$i'] | join(", ")' <<< "$project"); i=$((i + 1))
|
|
||||||
cat <<EOF
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<a href="$html_url">
|
|
||||||
<h2>$name</h2>
|
|
||||||
</a>
|
|
||||||
<a href="$html_url">
|
|
||||||
<img src="$avatar_url" alt="Avatar" width="100" height="100" alt="$name (logo)">
|
|
||||||
</a>
|
|
||||||
<p>$description</p>
|
|
||||||
<ul>
|
|
||||||
EOF
|
|
||||||
while read -r language; do
|
|
||||||
echo "<li class='language'>$language</li>"
|
|
||||||
done <<< "$languages_percentages"
|
|
||||||
cat <<EOF
|
|
||||||
</ul>
|
|
||||||
<pre>git clone $clone_url</pre>
|
|
||||||
<p>Last Updated: $updated_at</p>
|
|
||||||
<p>Licenses: $licenses</p>
|
|
||||||
</section>
|
|
||||||
EOF
|
|
||||||
done
|
|
||||||
echo '</div>'
|
|
||||||
|
|||||||
30
project-style.css
Normal file
30
project-style.css
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#quarto-document-content > pre:nth-child(-n+8) {
|
||||||
|
background: wheat;
|
||||||
|
border: 3px dashed pink;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
#quarto-document-content {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#quarto-document-content>* {
|
||||||
|
text-align: initial;
|
||||||
|
}
|
||||||
|
#quarto-document-content > ul:nth-child(-n+8) {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: center;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
#quarto-document-content > div.quarto-figure:nth-child(-n+8) {
|
||||||
|
width: 300px;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
#quarto-document-content > div.quarto-figure:nth-child(-n+8) figcaption {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#quarto-document-content > p:nth-child(-n+3) {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#quarto-document-content > p:first-child {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user