chore: init first alpha
All checks were successful
ci/woodpecker/push/commit Pipeline was successful
All checks were successful
ci/woodpecker/push/commit Pipeline was successful
This commit is contained in:
commit
45d9761be1
19 changed files with 3382 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
public/
|
||||
docker-compose.yml
|
||||
run.sh
|
||||
layout/node_modules
|
||||
# Added by goreleaser init:
|
||||
dist/
|
||||
36
.goreleaser.yaml
Normal file
36
.goreleaser.yaml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# This is an example .goreleaser.yml file with some sensible defaults.
|
||||
# Make sure to check the documentation at https://goreleaser.com
|
||||
|
||||
# The lines below are called `modelines`. See `:help modeline`
|
||||
# Feel free to remove those if you don't want/need to use them.
|
||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||
# vim: set ts=2 sw=2 tw=0 fo=cnqoj
|
||||
|
||||
version: 2
|
||||
|
||||
before:
|
||||
hooks:
|
||||
# You may remove this if you don't use go modules.
|
||||
- go mod tidy
|
||||
|
||||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- "^docs:"
|
||||
- "^test:"
|
||||
- "^ci:"
|
||||
|
||||
release:
|
||||
footer: >-
|
||||
|
||||
---
|
||||
|
||||
Released by [GoReleaser](https://github.com/goreleaser/goreleaser).
|
||||
16
.woodpecker/commit.yml
Normal file
16
.woodpecker/commit.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
when:
|
||||
- event: push
|
||||
branch: main
|
||||
|
||||
steps:
|
||||
- name: test
|
||||
image: golang:1.24
|
||||
commands:
|
||||
- go test ./...
|
||||
- name: build tailwind css
|
||||
image: codeberg.org/woodpecker-plugins/node-pm
|
||||
commands:
|
||||
- cd layout
|
||||
- npm install
|
||||
- npx @tailwindcss/cli -i style.css -o output.css
|
||||
|
||||
0
Readme.md
Normal file
0
Readme.md
Normal file
37
go.mod
Normal file
37
go.mod
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
module git.keks.cloud/kekskurse/wikipress
|
||||
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/urfave/cli/v3 v3.3.8
|
||||
github.com/yassinebenaid/godump v0.11.1
|
||||
github.com/yuin/goldmark v1.7.12
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
110
go.sum
Normal file
110
go.sum
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/urfave/cli/v3 v3.3.8 h1:BzolUExliMdet9NlJ/u4m5vHSotJ3PzEqSAZ1oPMa/E=
|
||||
github.com/urfave/cli/v3 v3.3.8/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/yassinebenaid/godump v0.11.1 h1:SPujx/XaYqGDfmNh7JI3dOyCUVrG0bG2duhO3Eh2EhI=
|
||||
github.com/yassinebenaid/godump v0.11.1/go.mod h1:dc/0w8wmg6kVIvNGAzbKH1Oa54dXQx8SNKh4dPRyW44=
|
||||
github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
|
||||
github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
1008
layout/output.css
Normal file
1008
layout/output.css
Normal file
File diff suppressed because it is too large
Load diff
1295
layout/package-lock.json
generated
Normal file
1295
layout/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
7
layout/package.json
Normal file
7
layout/package.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"@tailwindcss/cli": "^4.1.11",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"tailwindcss": "^4.1.11"
|
||||
}
|
||||
}
|
||||
143
layout/src/default.html
Normal file
143
layout/src/default.html
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Mein Wiki</title>
|
||||
<link rel="stylesheet" href="/output.css" />
|
||||
<script>
|
||||
function toggleSubtree(id) {
|
||||
const el = document.getElementById(id);
|
||||
const icon = document.getElementById('icon-' + id);
|
||||
if (el.classList.contains('hidden')) {
|
||||
el.classList.remove('hidden');
|
||||
icon.textContent = '▼';
|
||||
} else {
|
||||
el.classList.add('hidden');
|
||||
icon.textContent = '▶';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="bg-gray-100 text-gray-900 font-sans flex flex-col min-h-screen">
|
||||
<header class="md:hidden flex items-center justify-between bg-white border-b p-4">
|
||||
<h1 class="text-lg font-bold">Wiki</h1>
|
||||
<button id="menu-btn" class="focus:outline-none">
|
||||
<!-- einfaches SVG-Icon -->
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<div class="flex flex-1">
|
||||
<!-- Sidebar -->
|
||||
<aside id="sidebar"
|
||||
class="fixed inset-y-0 left-0 z-40 w-64 bg-gray-200 border-r border-gray-400 p-4 overflow-y-auto
|
||||
transform -translate-x-full transition-transform duration-200 ease-in-out
|
||||
md:relative md:translate-x-0 md:transform-none">
|
||||
<!-- Dein Menü hier kopieren -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h1 class="text-xl font-bold">Wiki-Menü</h1>
|
||||
</div>
|
||||
<nav class="space-y-2 text-sm">
|
||||
<ul class="space-y-1">
|
||||
{{- $global := . -}}
|
||||
{{define "node"}}
|
||||
{{- $ctx := . -}}
|
||||
{{range $ctx.Menu.Children }}
|
||||
{{if .Page}}
|
||||
<li>
|
||||
<a href="{{ .Link }}" class="block px-2 py-1 rounded hover:bg-gray-300">{{ .Name }}</a>
|
||||
</li>
|
||||
{{ else }}
|
||||
<li>
|
||||
<button onclick="toggleSubtree('{{ .ID }}')" class="w-full text-left px-2 py-1 rounded hover:bg-gray-300 flex items-center">
|
||||
<span id="icon-{{ .ID }}" class="mr-2">{{if ne (index $ctx.Folder .Level) .Name}}▶{{else}}▼{{end}}</span>{{ .Text }}
|
||||
</button>
|
||||
<ul id="{{ .ID }}" class="ml-4 mt-1 {{if ne (index $ctx.Folder .Level) .Name}}hidden{{end}} space-y-1">
|
||||
{{template "node" (dict "Menu" . "Folder" $ctx.Folder) }}
|
||||
</ul>
|
||||
</li>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{template "node" (dict "Menu" .Menu "Folder" .Folders) }}
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div id="overlay"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 z-30 opacity-0 pointer-events-none
|
||||
transition-opacity duration-200 ease-in-out
|
||||
md:hidden"></div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="flex-1 p-8 overflow-y-auto">
|
||||
<div class="max-w-screen-xl mx-auto">
|
||||
<h1 class="text-4xl font-bold mb-6 float-left">{{ .Title }}</h1><h5 class="float-right">from {{ .Version.Author.Name}} at {{ .Version.Date }}</h5>
|
||||
<div class="clear-both"></div>
|
||||
<div>
|
||||
<div class="border-b border-gray-300 mb-4">
|
||||
<nav class="flex space-x-4" id="tabs">
|
||||
<button class="tab px-4 py-2 text-sm font-medium border-b-2 border-gray-900" onclick="showTab('inhalt')">Inhalt</button>
|
||||
<button class="tab px-4 py-2 text-sm font-medium text-gray-600 hover:text-gray-800" onclick="showTab('history')">Historie</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div id="inhalt">
|
||||
<div class="prose max-w-none">
|
||||
{{ .HTMLContent }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="history" class="hidden">
|
||||
<h3 class="text-2xl font-serif font-semibold mb-4">Versionshistorie</h3>
|
||||
<ul class="list-disc list-inside text-gray-800">
|
||||
{{ range .File.Versions }}
|
||||
<li><strong>{{ .Message }}</strong> from {{ .Author.Name}} at {{ .Date }}</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
function showTab(tabId) {
|
||||
document.getElementById('inhalt').classList.add('hidden');
|
||||
document.getElementById('history').classList.add('hidden');
|
||||
document.getElementById(tabId).classList.remove('hidden');
|
||||
|
||||
document.querySelectorAll('#tabs .tab').forEach(btn => btn.classList.remove('border-gray-900'));
|
||||
event.target.classList.add('border-gray-900');
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
const btn = document.getElementById('menu-btn');
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const overlay = document.getElementById('overlay');
|
||||
|
||||
function toggleMenu() {
|
||||
sidebar.classList.toggle('-translate-x-full');
|
||||
overlay.classList.toggle('opacity-0');
|
||||
overlay.classList.toggle('pointer-events-none');
|
||||
}
|
||||
|
||||
btn.addEventListener('click', toggleMenu);
|
||||
overlay.addEventListener('click', toggleMenu);
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<!--<footer class="bg-gray-300 text-center py-4 border-t border-gray-400">
|
||||
<div class="flex justify-center space-x-8 text-sm">
|
||||
<a href="#" class="hover:underline">Impressum</a>
|
||||
<a href="#" class="hover:underline">Wikipress</a>
|
||||
<a href="#" class="hover:underline">Contact</a>
|
||||
</div>
|
||||
</footer>-->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
2
layout/style.css
Normal file
2
layout/style.css
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
@import "tailwindcss";
|
||||
@plugin "@tailwindcss/typography";
|
||||
87
main.go
Normal file
87
main.go
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.keks.cloud/kekskurse/wikipress/pkg/wikipress"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := &cli.Command{
|
||||
Name: "generate",
|
||||
Usage: "generate html from git folder",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "path",
|
||||
Value: "content",
|
||||
Usage: "Path to the content git repro",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "out",
|
||||
Value: "./public",
|
||||
Usage: "Path to the content git repro",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "no-git",
|
||||
Value: false,
|
||||
Usage: "dont use git, just current state",
|
||||
},
|
||||
},
|
||||
Action: action,
|
||||
}
|
||||
|
||||
if err := cmd.Run(context.Background(), os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func action(ctx context.Context, cmd *cli.Command) error {
|
||||
os.MkdirAll(cmd.String("path"), 0755)
|
||||
var files []wikipress.File
|
||||
var err error
|
||||
if cmd.Bool("no-git") {
|
||||
files, err = wikipress.ListFilesFromHDD(cmd.String("path"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
files, err = wikipress.ListFilesFromGitRepro(cmd.String("path"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
menu := wikipress.GenerateMenu(files)
|
||||
|
||||
for _, file := range files {
|
||||
// Copy not mark down files
|
||||
if file.GetExtension() != "md" {
|
||||
err = wikipress.CreateFilePageAndCopyContent(file, cmd.String("out"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Symlink(fmt.Sprintf("./%v_%v.%v", file.GetName(), file.GetLastVersion().Hash, file.GetExtension()), fmt.Sprintf("%v/%v/%v.%v", cmd.String("out"), file.GetFolder(), file.GetName(), file.GetExtension()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: Copy and create media page
|
||||
continue
|
||||
}
|
||||
|
||||
err = wikipress.GenerateHTMLPageForMarkedown(file, menu, cmd.String("out"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Symlink(fmt.Sprintf("./%v_%v.html", file.GetName(), file.GetLastVersion().Hash), fmt.Sprintf("%v/%v/%v.html", cmd.String("out"), file.GetFolder(), file.GetName()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
9
pkg/wikipress/error.go
Normal file
9
pkg/wikipress/error.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package wikipress
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrCantReadGitRepro = errors.New("Cant read git repro")
|
||||
ErrCantRenderNonMarktdownFile = errors.New("Cant render non markdown files")
|
||||
ErrCantParseMarkdownToHTML = errors.New("Cant render markdown to html")
|
||||
)
|
||||
55
pkg/wikipress/file.go
Normal file
55
pkg/wikipress/file.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package wikipress
|
||||
|
||||
import "strings"
|
||||
|
||||
type File struct {
|
||||
Name string
|
||||
Deleted bool
|
||||
Versions []Version
|
||||
}
|
||||
|
||||
func (f File) GetExtension() string {
|
||||
lastDot := strings.LastIndex(f.Name, ".")
|
||||
if lastDot == -1 {
|
||||
return ""
|
||||
}
|
||||
return f.Name[lastDot+1:]
|
||||
}
|
||||
|
||||
func (f File) GetFolder() string {
|
||||
lastSlash := strings.LastIndex(f.Name, "/")
|
||||
if lastSlash == -1 {
|
||||
return ""
|
||||
}
|
||||
return f.Name[0:lastSlash]
|
||||
}
|
||||
|
||||
func (f File) GetName() string {
|
||||
lastSlash := strings.LastIndex(f.Name, "/")
|
||||
lastDot := strings.LastIndex(f.Name, ".")
|
||||
|
||||
return f.Name[lastSlash+1 : lastDot]
|
||||
}
|
||||
|
||||
func (f File) GetFolders() []string {
|
||||
folder := f.GetFolder()
|
||||
res := strings.Split(folder, "/")
|
||||
return res
|
||||
}
|
||||
|
||||
func (f File) GetTitle() string {
|
||||
title := f.GetLastVersion().GetHeader("title", f.GetName())
|
||||
return title
|
||||
}
|
||||
|
||||
func (f File) GetLastVersion() Version {
|
||||
return f.Versions[len(f.Versions)-1]
|
||||
}
|
||||
|
||||
func (f File) GetMenuPosition() int {
|
||||
if len(f.Versions) == 0 {
|
||||
return 1000
|
||||
}
|
||||
|
||||
return f.GetLastVersion().GetHeaderInt("order", 1500)
|
||||
}
|
||||
51
pkg/wikipress/file_test.go
Normal file
51
pkg/wikipress/file_test.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package wikipress
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetFileExtension(t *testing.T) {
|
||||
tts := []struct {
|
||||
name string
|
||||
file File
|
||||
expExtension string
|
||||
}{
|
||||
{
|
||||
name: "markedown file",
|
||||
file: File{
|
||||
Name: "home.md",
|
||||
},
|
||||
expExtension: "md",
|
||||
},
|
||||
{
|
||||
name: "multible dot",
|
||||
file: File{
|
||||
Name: "foo.br.jpg",
|
||||
},
|
||||
expExtension: "jpg",
|
||||
},
|
||||
{
|
||||
name: "with folder",
|
||||
file: File{
|
||||
Name: "foo/bar/test.txt",
|
||||
},
|
||||
expExtension: "txt",
|
||||
},
|
||||
{
|
||||
name: "no dot",
|
||||
file: File{
|
||||
Name: "something",
|
||||
},
|
||||
expExtension: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tts {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
extension := tt.file.GetExtension()
|
||||
assert.Equal(t, tt.expExtension, extension, "should be right extension")
|
||||
})
|
||||
}
|
||||
}
|
||||
193
pkg/wikipress/generator.go
Normal file
193
pkg/wikipress/generator.go
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
package wikipress
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func CreateFilePageAndCopyContent(file File, outfolder string) error {
|
||||
if file.GetExtension() == "md" {
|
||||
return nil
|
||||
}
|
||||
|
||||
for versionNumber := range file.Versions {
|
||||
folder := fmt.Sprintf("%v/%v", outfolder, file.GetFolder())
|
||||
path := fmt.Sprintf("%v/%v_%v.%v", folder, file.GetName(), file.Versions[versionNumber].Hash, file.GetExtension())
|
||||
err := os.WriteFile(path, file.Versions[versionNumber].contentbinary, 0o644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GenerateHTMLPageForMarkedown(file File, menu MenuItem, outfolder string) error {
|
||||
os.MkdirAll(outfolder, 0755)
|
||||
if file.GetExtension() != "md" {
|
||||
return ErrCantRenderNonMarktdownFile
|
||||
}
|
||||
|
||||
for versionnumber := range file.Versions {
|
||||
err := renderHTML(file, menu, versionnumber, outfolder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type MenuItem struct {
|
||||
ID string
|
||||
Name string
|
||||
Text string
|
||||
Link string
|
||||
File File
|
||||
Level int
|
||||
Children []*MenuItem
|
||||
Page bool
|
||||
}
|
||||
|
||||
func GenerateMenu(files []File) MenuItem {
|
||||
menu := MenuItem{}
|
||||
for _, file := range files {
|
||||
if file.Deleted {
|
||||
continue
|
||||
}
|
||||
if file.GetExtension() != "md" {
|
||||
continue
|
||||
}
|
||||
folders := file.GetFolders()
|
||||
folders = append(folders, "")
|
||||
currentMenu := &menu
|
||||
for level, folderLevel := range folders {
|
||||
if folderLevel == "" {
|
||||
newMenuItem := MenuItem{}
|
||||
newMenuItem.Name = file.GetTitle()
|
||||
newMenuItem.Page = true
|
||||
newMenuItem.File = file
|
||||
newMenuItem.Level = level
|
||||
if file.GetFolder() != "" {
|
||||
newMenuItem.Link = "/" + file.GetFolder()
|
||||
}
|
||||
newMenuItem.Link = newMenuItem.Link + "/" + file.GetName() + ".html"
|
||||
currentMenu.Children = append(currentMenu.Children, &newMenuItem)
|
||||
break
|
||||
// Create new Item
|
||||
} else {
|
||||
found := false
|
||||
for _, m := range currentMenu.Children {
|
||||
if m.Name == folderLevel {
|
||||
currentMenu = m
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
newMenuItem := MenuItem{}
|
||||
newMenuItem.ID = uuid.NewString()
|
||||
newMenuItem.Name = folderLevel
|
||||
newMenuItem.Level = level
|
||||
newMenuItem.Text = strings.Title(folderLevel)
|
||||
currentMenu.Children = append(currentMenu.Children, &newMenuItem)
|
||||
currentMenu = &newMenuItem
|
||||
}
|
||||
// Get children or create a new one if needed
|
||||
}
|
||||
}
|
||||
}
|
||||
menu.Children = sortMenu(menu.Children)
|
||||
return menu
|
||||
}
|
||||
|
||||
func sortMenu(input []*MenuItem) []*MenuItem {
|
||||
sort.Slice(input, func(i, j int) bool {
|
||||
return strings.ToLower(input[i].Name) < strings.ToLower(input[j].Name)
|
||||
})
|
||||
|
||||
sort.Slice(input, func(i, j int) bool {
|
||||
if input[i].File.GetMenuPosition() < input[j].File.GetMenuPosition() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
for _, i := range input {
|
||||
i.Children = sortMenu(i.Children)
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
type htmlTemplateData struct {
|
||||
HTMLContent template.HTML
|
||||
Title string
|
||||
File File
|
||||
Menu MenuItem
|
||||
Folders []string
|
||||
Version Version
|
||||
}
|
||||
|
||||
func renderHTML(file File, menu MenuItem, versionNumber int, outfolder string) error {
|
||||
folder := fmt.Sprintf("%v/%v", outfolder, file.GetFolder())
|
||||
htmlPath := fmt.Sprintf("%v/%v_%v.html", folder, file.GetName(), file.Versions[versionNumber].Hash)
|
||||
fmt.Println(htmlPath)
|
||||
os.MkdirAll(folder, 0755)
|
||||
|
||||
var err error
|
||||
h := htmlTemplateData{}
|
||||
h.Menu = menu
|
||||
h.Title = file.Versions[versionNumber].GetHeader("title", file.GetName())
|
||||
h.File = file
|
||||
h.Folders = file.GetFolders()
|
||||
h.Version = file.Versions[versionNumber]
|
||||
content, err := file.Versions[versionNumber].GetHTMLContent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.HTMLContent = template.HTML(content)
|
||||
|
||||
tmpl := template.New("default.html")
|
||||
|
||||
tmpl.Funcs(template.FuncMap{
|
||||
"dict": dict,
|
||||
"index": index,
|
||||
})
|
||||
|
||||
tmpl, err = tmpl.ParseFiles("layout/src/default.html")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outFile, err := os.Create(htmlPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tmpl.Execute(outFile, h)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dict(values ...any) map[string]any {
|
||||
m := make(map[string]any)
|
||||
for i := 0; i < len(values); i += 2 {
|
||||
m[values[i].(string)] = values[i+1]
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func index(slice []string, i int) string {
|
||||
if i < 0 || i >= len(slice) {
|
||||
return ""
|
||||
}
|
||||
return slice[i]
|
||||
}
|
||||
150
pkg/wikipress/git.go
Normal file
150
pkg/wikipress/git.go
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package wikipress
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
)
|
||||
|
||||
func ListFilesFromHDD(opath string) ([]File, error) {
|
||||
opath = opath + "/"
|
||||
FileList := []File{}
|
||||
err := filepath.Walk(opath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err // z.B. fehlende Leserechte
|
||||
}
|
||||
if !info.IsDir() {
|
||||
v := Version{}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.content = string(data)
|
||||
v.contentbinary = data
|
||||
v.Author.Name = "HDD"
|
||||
v.Date = time.Now()
|
||||
v.Hash = "abc"
|
||||
v.Message = "test"
|
||||
|
||||
f := File{}
|
||||
f.Name = strings.TrimPrefix(path, opath)
|
||||
f.Versions = append(f.Versions, v)
|
||||
|
||||
FileList = append(FileList, f)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return FileList, err
|
||||
}
|
||||
|
||||
func ListFilesFromGitRepro(path string) ([]File, error) {
|
||||
repo, err := git.PlainOpen(path)
|
||||
if err != nil {
|
||||
return []File{}, fmt.Errorf("%w: %s", ErrCantReadGitRepro, err)
|
||||
}
|
||||
|
||||
ref, err := repo.Head()
|
||||
if err != nil {
|
||||
return []File{}, fmt.Errorf("%w: %s", ErrCantReadGitRepro, err)
|
||||
}
|
||||
|
||||
commitIter, err := repo.Log(&git.LogOptions{From: ref.Hash()})
|
||||
if err != nil {
|
||||
return []File{}, fmt.Errorf("%w: %s", ErrCantReadGitRepro, err)
|
||||
}
|
||||
|
||||
var commits []*object.Commit
|
||||
err = commitIter.ForEach(func(c *object.Commit) error {
|
||||
commits = append(commits, c)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return []File{}, fmt.Errorf("%w: %s", ErrCantReadGitRepro, err)
|
||||
}
|
||||
|
||||
for i, j := 0, len(commits)-1; i < j; i, j = i+1, j-1 {
|
||||
commits[i], commits[j] = commits[j], commits[i]
|
||||
}
|
||||
|
||||
lastContent := make(map[string]string)
|
||||
fileList := make(map[string]File)
|
||||
currentFileList := make(map[string]bool)
|
||||
|
||||
for _, c := range commits {
|
||||
|
||||
tree, err := c.Tree()
|
||||
if err != nil {
|
||||
return []File{}, err
|
||||
}
|
||||
|
||||
currentFileList = make(map[string]bool)
|
||||
|
||||
err = tree.Files().ForEach(func(f *object.File) error {
|
||||
path := filepath.ToSlash(f.Name)
|
||||
currentFileList[f.Name] = true
|
||||
content, err := f.Contents()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if lastContent[path] == content {
|
||||
return nil
|
||||
}
|
||||
|
||||
byteContentReader, err := f.Reader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
byteContent, err := io.ReadAll(byteContentReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lastContent[path] = content
|
||||
|
||||
version := Version{}
|
||||
version.Author.Name = c.Author.Name
|
||||
version.Message = c.Message
|
||||
version.Date = c.Author.When
|
||||
version.content = content
|
||||
version.contentbinary = byteContent
|
||||
version.Hash = c.Hash.String()
|
||||
|
||||
file, ok := fileList[path]
|
||||
if !ok {
|
||||
file.Name = f.Name
|
||||
}
|
||||
|
||||
file.Versions = append(file.Versions, version)
|
||||
|
||||
fileList[path] = file
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return []File{}, err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return []File{}, fmt.Errorf("%w: %s", ErrCantReadGitRepro, err)
|
||||
}
|
||||
|
||||
fileListReturn := []File{}
|
||||
for _, f := range fileList {
|
||||
_, ok := currentFileList[f.Name]
|
||||
if !ok {
|
||||
f.Deleted = true
|
||||
}
|
||||
fileListReturn = append(fileListReturn, f)
|
||||
}
|
||||
|
||||
return fileListReturn, nil
|
||||
}
|
||||
99
pkg/wikipress/version.go
Normal file
99
pkg/wikipress/version.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package wikipress
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
"github.com/yuin/goldmark/renderer/html"
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
Author struct {
|
||||
Name string
|
||||
}
|
||||
Message string
|
||||
Date time.Time
|
||||
content string
|
||||
contentbinary []byte
|
||||
Hash string
|
||||
}
|
||||
|
||||
func (v Version) GetBody() string {
|
||||
details := strings.SplitN(v.content, "---", 2)
|
||||
if len(details) == 2 {
|
||||
return details[1]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (v Version) GetHeaders() map[string]string {
|
||||
details := strings.SplitN(v.content, "---", 2)
|
||||
if len(details) < 2 {
|
||||
return make(map[string]string)
|
||||
}
|
||||
|
||||
lines := strings.Split(details[0], "\n")
|
||||
headers := make(map[string]string)
|
||||
for _, line := range lines {
|
||||
lineContent := strings.SplitN(line, ":", 2)
|
||||
if len(lineContent) != 2 {
|
||||
continue
|
||||
}
|
||||
headers[lineContent[0]] = strings.TrimSpace(lineContent[1])
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func (v Version) GetHeader(name, defaultValue string) string {
|
||||
headers := v.GetHeaders()
|
||||
val, ok := headers[name]
|
||||
if ok {
|
||||
return val
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func (v Version) GetHeaderInt(name string, defaultValue int) int {
|
||||
headers := v.GetHeaders()
|
||||
val, ok := headers[name]
|
||||
if ok {
|
||||
vali, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
}
|
||||
return vali
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func (v Version) GetHTMLContent() (string, error) {
|
||||
mdParser := goldmark.New(
|
||||
goldmark.WithExtensions(
|
||||
extension.GFM, // GitHub-Flavored Markdown (inkl. Tabellen, Tasklists)
|
||||
extension.Footnote, // Fußnoten
|
||||
extension.Table, // Tabellen (redundant, aber explizit)
|
||||
extension.Strikethrough, // Durchgestrichener Text
|
||||
extension.DefinitionList,
|
||||
extension.TaskList,
|
||||
),
|
||||
goldmark.WithRendererOptions(
|
||||
html.WithHardWraps(),
|
||||
html.WithUnsafe(), // erlaubt raw HTML (vorsichtig!)
|
||||
),
|
||||
)
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := mdParser.Convert([]byte(v.GetBody()), &buf)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w: %s", ErrCantParseMarkdownToHTML, err)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
78
pkg/wikipress/version_test.go
Normal file
78
pkg/wikipress/version_test.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package wikipress
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetody(t *testing.T) {
|
||||
tts := []struct {
|
||||
name string
|
||||
version Version
|
||||
expBody string
|
||||
}{
|
||||
{
|
||||
name: "simple content",
|
||||
version: Version{content: "foobar\r\n---\r\nBlaBla"},
|
||||
expBody: "\r\nBlaBla",
|
||||
},
|
||||
{
|
||||
name: "file without body",
|
||||
version: Version{content: "blabla"},
|
||||
expBody: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tts {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
body := tt.version.GetBody()
|
||||
assert.Equal(t, tt.expBody, body, "should return currect body")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHeaders(t *testing.T) {
|
||||
expData1 := make(map[string]string)
|
||||
expData1["foo"] = "bar"
|
||||
tts := []struct {
|
||||
name string
|
||||
version Version
|
||||
expHeader map[string]string
|
||||
}{
|
||||
{
|
||||
name: "simple test",
|
||||
version: Version{content: "foo: bar\r\n---\r\ncontent"},
|
||||
expHeader: expData1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tts {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
headers := tt.version.GetHeaders()
|
||||
assert.Equal(t, tt.expHeader, headers, "headers should match")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetHTMLContent(t *testing.T) {
|
||||
tts := []struct {
|
||||
name string
|
||||
version Version
|
||||
exptHTML string
|
||||
}{
|
||||
{
|
||||
name: "simeple",
|
||||
version: Version{content: "foo:bar\r\n---\r\n# Simple\r\nTest"},
|
||||
exptHTML: "<h1>Simple</h1>\n<p>Test</p>\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tts {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
html, err := tt.version.GetHTMLContent()
|
||||
assert.Nil(t, err, "should return no error")
|
||||
assert.Equal(t, tt.exptHTML, html, "should return expected html")
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue