Detailed changes
@@ -7,7 +7,7 @@ tmp_dir = "tmp"
[build]
bin = "./tmp/willow"
- cmd = "go build -o ./tmp/willow ./cmd"
+ cmd = "go build -o ./tmp/willow -ldflags \"-X main.version=`git describe --long 2>/dev/null | sed 's/\\([^-]*-g\\)/r\\1/;s/-/./g'`\" ./cmd"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata", "data", "website"]
exclude_file = []
@@ -0,0 +1,59 @@
+{{- template "head" }}
+{{- template "header" }}
+ <main>
+ <div class="wrapper two_column">
+ <div class="projects">
+ <!-- Range through projects that aren't yet up-to-date -->
+ {{- range .Projects -}}
+ {{- if ne .Running (index .Releases 0).Tag -}}
+ <h2>Outdated projects</h2>
+ {{- break -}}
+ {{- end -}}
+ {{- end -}}
+ {{- range .Projects -}}
+ {{- if ne .Running (index .Releases 0).Tag -}}
+ <div id="{{ .ID }}" class="project card">
+ <h3><a href="{{ .URL }}">{{ .Name }}</a> <span class="delete"><a href="/new?action=delete&id={{ .ID }}">Delete?</a></span></h3>
+ <p>You've selected {{ .Running }}. <a href="/new?action=update&url={{ .URL }}&forge={{ .Forge }}&name={{ .Name }}">Modify?</a></p>
+ <p>Latest: <a href="{{ (index .Releases 0).URL }}">{{ (index .Releases 0).Tag }}</a></p>
+ <p><a href="#{{ (index .Releases 0).ID }}">View release notes</a></p>
+ </div>
+ {{- end -}}
+ {{- end -}}
+
+ <!-- Range through projects that _are_ up-to-date -->
+ {{- range .Projects -}}
+ {{- if eq .Running (index .Releases 0).Tag -}}
+ <h2>Up-to-date projects</h2>
+ {{- break -}}
+ {{- end -}}
+ {{- end -}}
+ {{- range .Projects -}}
+ {{- if eq .Running (index .Releases 0).Tag -}}
+ <div class="project card">
+ <h3><a href="{{ .URL }}">{{ .Name }}</a> <span class="delete"><a href="/new?action=delete&id={{ .ID }}">Delete?</a></span></h3>
+ <p>You've selected <a href="#{{ (index .Releases 0).ID }}">{{ .Running }}</a>. <a href="/new?action=update&url={{ .URL }}&forge={{ .Forge }}&name={{ .Name }}">Modify?</a></p>
+ </div>
+ {{- end -}}
+ {{- end -}}
+ </div>
+ <div class="release_notes">
+ <h2>Release notes</h2>
+ {{- range .Projects -}}
+ <div id="{{ (index .Releases 0).ID }}" class="release_note card">
+ <h3>{{ .Name }}: release notes for <a href="{{ (index .Releases 0).URL }}">{{ (index .Releases 0).Tag }}</a></h3>
+ {{- if eq .Forge "github" "gitea" "forgejo" -}}
+ {{- (index .Releases 0).Content -}}
+ {{- else -}}
+ <pre>
+ {{- (index .Releases 0).Content -}}
+ </pre>
+ {{- end -}}
+ <p><a class="return_to_project" href="#{{ .ID }}">Back to project</a></p>
+ <div class="close"><a href="#">✖</a></div>
+ </div>
+ {{- end -}}
+ </div>
+ </div>
+ </main>
+{{- template "footer" .Version }}
@@ -0,0 +1,9 @@
+{{- define "footer" }}
+ <footer>
+ <div class="wrapper">
+ <p>Willow {{ . }} — <a href="https://getwillow.org">Source code</a> — <a href="https://todo.sr.ht/~amolith/willow">Issue queue</a></p>
+ </div>
+ </footer>
+ </body>
+</html>
+{{- end -}}
@@ -1,3 +1,4 @@
+{{- define "head" }}
<!DOCTYPE html>
<html lang="en-GB">
<head>
@@ -19,19 +20,5 @@
<link rel="preload" href="/static/styles.css" as="style" />
<link rel="stylesheet" href="/static/styles.css" />
</head>
- <body class="old-wrapper">
- <h1>Willow</h1>
- <form method="post">
- <div class="input">
- <label for="username">Username:</label>
- <input type="text" id="username" name="username">
- </div>
- <div class="input">
- <label for="password">Password:</label>
- <input type="password" id="password" name="password">
- </div>
- <input class="button" type="submit" formaction="/login" value="Login">
- </form>
- <p><a href="https://sr.ht/~amolith/willow">Source code</a></p>
- </body>
-</html>
+ <body>
+{{- end -}}
@@ -0,0 +1,11 @@
+{{- define "header" }}
+ <header>
+ <div class="wrapper">
+ <h1>Willow</h1>
+ <nav>
+ <a href="/new">Track a new project</a>
+ <a href="/logout">Log out</a>
+ </nav>
+ </div>
+ </header>
+{{- end -}}
@@ -1,96 +0,0 @@
-<!DOCTYPE html>
-<html lang="en-GB">
- <head>
- <title>Willow</title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta name="title" content="Willow">
- <meta name="description" content="Forge-agnostic software release tracker">
-
- <!-- Indicate that we support both light and dark mode -->
- <meta name="color-scheme" content="dark light">
-
- <!-- Preload CSS reset -->
- <link rel="preload" href="/static/reset.css" as="style" />
- <link rel="stylesheet" href="/static/reset.css" />
-
- <!-- Preload CSS styles -->
- <link rel="preload" href="/static/colours.css" as="style" />
- <link rel="stylesheet" href="/static/colours.css" />
- <link rel="preload" href="/static/styles.css" as="style" />
- <link rel="stylesheet" href="/static/styles.css" />
- </head>
- <body>
- <div class="container">
- <header>
- <div class="wrapper">
- <h1>Willow</h1>
- <nav>
- <a href="/new">Track a new project</a>
- <a href="/logout">Log out</a>
- </nav>
- </div>
- </header>
- <main>
- <div class="wrapper two_column">
- <div class="projects">
- <!-- Range through projects that aren't yet up-to-date -->
- {{- range .Projects -}}
- {{- if ne .Running (index .Releases 0).Tag -}}
- <h2>Outdated projects</h2>
- {{- break -}}
- {{- end -}}
- {{- end -}}
- {{- range .Projects -}}
- {{- if ne .Running (index .Releases 0).Tag -}}
- <div id="{{ .ID }}" class="project card">
- <h3><a href="{{ .URL }}">{{ .Name }}</a> <span class="delete"><a href="/new?action=delete&id={{ .ID }}">Delete?</a></span></h3>
- <p>You've selected {{ .Running }}. <a href="/new?action=update&url={{ .URL }}&forge={{ .Forge }}&name={{ .Name }}">Modify?</a></p>
- <p>Latest: <a href="{{ (index .Releases 0).URL }}">{{ (index .Releases 0).Tag }}</a></p>
- <p><a href="#{{ (index .Releases 0).ID }}">View release notes</a></p>
- </div>
- {{- end -}}
- {{- end -}}
-
- <!-- Range through projects that _are_ up-to-date -->
- {{- range .Projects -}}
- {{- if eq .Running (index .Releases 0).Tag -}}
- <h2>Up-to-date projects</h2>
- {{- break -}}
- {{- end -}}
- {{- end -}}
- {{- range .Projects -}}
- {{- if eq .Running (index .Releases 0).Tag -}}
- <div class="project card">
- <h3><a href="{{ .URL }}">{{ .Name }}</a> <span class="delete"><a href="/new?action=delete&id={{ .ID }}">Delete?</a></span></h3>
- <p>You've selected <a href="#{{ (index .Releases 0).ID }}">{{ .Running }}</a>. <a href="/new?action=update&url={{ .URL }}&forge={{ .Forge }}&name={{ .Name }}">Modify?</a></p>
- </div>
- {{- end -}}
- {{- end -}}
- </div>
- <div class="release_notes">
- <h2>Release notes</h2>
- {{- range .Projects -}}
- <div id="{{ (index .Releases 0).ID }}" class="release_note card">
- <h3>{{ .Name }}: release notes for <a href="{{ (index .Releases 0).URL }}">{{ (index .Releases 0).Tag }}</a></h3>
- {{- if eq .Forge "github" "gitea" "forgejo" -}}
- {{- (index .Releases 0).Content -}}
- {{- else -}}
- <pre>
- {{- (index .Releases 0).Content -}}
- </pre>
- {{- end -}}
- <p><a class="return_to_project" href="#{{ .ID }}">Back to project</a></p>
- <div class="close"><a href="#">✖</a></div>
- </div>
- {{- end -}}
- </div>
- </div>
- </main>
- <footer>
- <div class="wrapper">
- <p>Willow {{ .Version }} — <a href="https://getwillow.org">Source code</a> — <a href="https://todo.sr.ht/~amolith/willow">Issue queue</a></p>
- </div>
- </footer>
- </div>
- </body>
-</html>
@@ -0,0 +1,20 @@
+{{- template "head" -}}
+ <header>
+ <div class="wrapper">
+ <h1>Willow</h1>
+ </div>
+ </header>
+ <div class="old-wrapper">
+ <form method="post">
+ <div class="input">
+ <label for="username">Username:</label>
+ <input type="text" id="username" name="username">
+ </div>
+ <div class="input">
+ <label for="password">Password:</label>
+ <input type="password" id="password" name="password">
+ </div>
+ <input class="button" type="submit" formaction="/login" value="Login">
+ </form>
+ </div>
+{{- template "footer" .Version -}}
@@ -0,0 +1,3 @@
+SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+
+SPDX-License-Identifier: Apache-2.0
@@ -1,26 +1,7 @@
-<!DOCTYPE html>
-<html lang="en-GB">
- <head>
- <title>Willow</title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta name="title" content="Willow">
- <meta name="description" content="Forge-agnostic software release tracker">
-
- <!-- Indicate that we support both light and dark mode -->
- <meta name="color-scheme" content="dark light">
-
- <!-- Preload CSS reset -->
- <link rel="preload" href="/static/reset.css" as="style" />
- <link rel="stylesheet" href="/static/reset.css" />
-
- <!-- Preload CSS styles -->
- <link rel="preload" href="/static/colours.css" as="style" />
- <link rel="stylesheet" href="/static/colours.css" />
- <link rel="preload" href="/static/styles.css" as="style" />
- <link rel="stylesheet" href="/static/styles.css" />
- </head>
- <body class="old-wrapper">
- <h1>Willow</h1>
+{{- template "head" }}
+{{- template "header" }}
+ <div class="old-wrapper">
+ <h2>Track a new project</h2>
<form method="post">
<div class="input">
<label for="url">Project URL:</label>
@@ -51,5 +32,5 @@
</div>
<input class="button" type="submit" formaction="/new" value="Next">
</form>
- </body>
-</html>
+ </div>
+{{- template "footer" .Version }}
@@ -0,0 +1,3 @@
+SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+
+SPDX-License-Identifier: Apache-2.0
@@ -1,26 +1,7 @@
-<!DOCTYPE html>
-<html lang="en-GB">
- <head>
- <title>Willow</title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta name="title" content="Willow">
- <meta name="description" content="Forge-agnostic software release tracker">
-
- <!-- Indicate that we support both light and dark mode -->
- <meta name="color-scheme" content="dark light">
-
- <!-- Preload CSS reset -->
- <link rel="preload" href="/static/reset.css" as="style" />
- <link rel="stylesheet" href="/static/reset.css" />
-
- <!-- Preload CSS styles -->
- <link rel="preload" href="/static/colours.css" as="style" />
- <link rel="stylesheet" href="/static/colours.css" />
- <link rel="preload" href="/static/styles.css" as="style" />
- <link rel="stylesheet" href="/static/styles.css" />
- </head>
- <body class="old-wrapper">
- <h1>Willow</h1>
+{{- template "head" }}
+{{- template "header" }}
+{{- with .Project }}
+ <div class="old-wrapper">
<form method="post">
<div class="input">
<p>Which release of {{ .Name }} are you currently running?</p>
@@ -52,5 +33,6 @@
{{- if or (eq $forge "github") -}}
<small>Some RSS feeds (notably GitHub's) include a limited number of releases. If you don't see your version, please change the forge type to "Other".</small>
{{- end -}}
- </body>
-</html>
+ </div>
+{{- end }}
+{{- template "footer" .Version -}}
@@ -0,0 +1,3 @@
+SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+
+SPDX-License-Identifier: Apache-2.0
@@ -53,34 +53,34 @@ a:visited {
}
/* Grid layout */
-.container {
+body {
width: auto;
min-height: 100vh;
}
@supports (display: grid) {
- .container {
+ body {
display: grid;
grid-template-rows: [header] auto [main] 1fr [footer] auto;
}
- .container > header,
- .container > main,
- .container > footer {
+ body > header,
+ body > main,
+ body > footer {
display: grid;
grid-template-columns:
[page-start] minmax(1em, 1fr) [content] minmax(240px, 100ch) [page-end] minmax(1em, 1fr);
}
- .container > main {
+ body > main {
grid-template-rows: [top-gutter] 1em [content] 1fr [bottom-gutter] 1em;
}
- .container > footer {
+ body > footer {
grid-template-rows: [top-gutter] 2em [content] 1fr [bottom-gutter] 0.5em;
}
- .container > header {
+ body > header {
grid-template-rows: [top-gutter] 0.5em [content] 1fr [bottom-gutter] 0.5em;
}
@@ -164,7 +164,7 @@ a:visited {
.old-wrapper { /* used on non-home pages */
max-width: 500px;
- margin: auto auto;
+ margin: 0 auto;
}
header .wrapper {
@@ -52,17 +52,15 @@ func (h Handler) RootHandler(w http.ResponseWriter, r *http.Request) {
return
}
- type stuff struct {
+ data := struct {
Version string
Projects []project.Project
- }
-
- data := stuff{
+ }{
Version: *h.Version,
Projects: projectsWithReleases,
}
- tmpl := template.Must(template.ParseFS(fs, "static/home.html"))
+ tmpl := template.Must(template.ParseFS(fs, "static/dashboard.html.tmpl", "static/head.html.tmpl", "static/header.html.tmpl", "static/footer.html.tmpl"))
if err := tmpl.Execute(w, data); err != nil {
fmt.Println(err)
}
@@ -77,8 +75,9 @@ func (h Handler) NewHandler(w http.ResponseWriter, r *http.Request) {
action := bmStrict.Sanitize(params.Get("action"))
if r.Method == http.MethodGet {
if action == "" {
- tmpl := template.Must(template.ParseFS(fs, "static/new.html"))
- if err := tmpl.Execute(w, nil); err != nil {
+ data := struct{ Version string }{Version: *h.Version}
+ tmpl := template.Must(template.ParseFS(fs, "static/new.html.tmpl", "static/head.html.tmpl", "static/header.html.tmpl", "static/footer.html.tmpl"))
+ if err := tmpl.Execute(w, data); err != nil {
fmt.Println(err)
}
} else if action != "delete" {
@@ -139,8 +138,16 @@ func (h Handler) NewHandler(w http.ResponseWriter, r *http.Request) {
return
}
- tmpl := template.Must(template.ParseFS(fs, "static/select-release.html"))
- if err := tmpl.Execute(w, proj); err != nil {
+ data := struct {
+ Version string
+ Project project.Project
+ }{
+ Version: *h.Version,
+ Project: proj,
+ }
+
+ tmpl := template.Must(template.ParseFS(fs, "static/select-release.html.tmpl", "static/head.html.tmpl", "static/header.html.tmpl", "static/footer.html.tmpl"))
+ if err := tmpl.Execute(w, data); err != nil {
fmt.Println(err)
}
} else if action == "delete" {
@@ -198,12 +205,13 @@ func (h Handler) LoginHandler(w http.ResponseWriter, r *http.Request) {
return
}
- login, err := fs.ReadFile("static/login.html")
- if err != nil {
- fmt.Println("Error reading login.html:", err)
+ data := struct {
+ Version string
+ }{
+ Version: *h.Version,
}
-
- if _, err := io.WriteString(w, string(login)); err != nil {
+ tmpl := template.Must(template.ParseFS(fs, "static/login.html.tmpl", "static/head.html.tmpl", "static/footer.html.tmpl"))
+ if err := tmpl.Execute(w, data); err != nil {
fmt.Println(err)
}
}
@@ -318,7 +326,7 @@ func StaticHandler(writer http.ResponseWriter, request *http.Request) {
if err != nil {
fmt.Println(err)
}
- if _, err = io.WriteString(writer, string(home)); err != nil {
+ if _, err = io.Writer.Write(writer, home); err != nil {
fmt.Println(err)
}
}