Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
caddy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Łukasz Nowak
caddy
Commits
236c8c4e
Commit
236c8c4e
authored
Aug 05, 2015
by
Matt Holt
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #214 from abiosoft/master
markdown: generate static sites after links
parents
96693635
3b910645
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
167 additions
and
141 deletions
+167
-141
config/setup/markdown.go
config/setup/markdown.go
+4
-54
middleware/markdown/generator.go
middleware/markdown/generator.go
+135
-0
middleware/markdown/markdown.go
middleware/markdown/markdown.go
+1
-1
middleware/markdown/markdown_test.go
middleware/markdown/markdown_test.go
+12
-9
middleware/markdown/page.go
middleware/markdown/page.go
+6
-60
middleware/markdown/testdata/og_static/og/first.md/index.html
...leware/markdown/testdata/og_static/og/first.md/index.html
+0
-13
middleware/markdown/watcher.go
middleware/markdown/watcher.go
+9
-4
No files found.
config/setup/markdown.go
View file @
236c8c4e
package
setup
package
setup
import
(
import
(
"io/ioutil"
"net/http"
"net/http"
"os"
"path"
"path"
"path/filepath"
"path/filepath"
"strings"
"strings"
...
@@ -32,63 +30,15 @@ func Markdown(c *Controller) (middleware.Middleware, error) {
...
@@ -32,63 +30,15 @@ func Markdown(c *Controller) (middleware.Middleware, error) {
for
i
:=
range
mdconfigs
{
for
i
:=
range
mdconfigs
{
cfg
:=
&
mdconfigs
[
i
]
cfg
:=
&
mdconfigs
[
i
]
//
Links generation
.
//
Generate static files
.
if
err
:=
markdown
.
Generate
Links
(
md
,
cfg
);
err
!=
nil
{
if
err
:=
markdown
.
Generate
Static
(
md
,
cfg
);
err
!=
nil
{
return
err
return
err
}
}
// Watch file changes for links generation if not in development mode.
// Watch file changes for static site generation if not in development mode.
if
!
cfg
.
Development
{
if
!
cfg
.
Development
{
markdown
.
Watch
(
md
,
cfg
,
markdown
.
DefaultInterval
)
markdown
.
Watch
(
md
,
cfg
,
markdown
.
DefaultInterval
)
}
}
if
cfg
.
StaticDir
==
""
{
continue
}
// If generated site already exists, clear it out
_
,
err
:=
os
.
Stat
(
cfg
.
StaticDir
)
if
err
==
nil
{
err
:=
os
.
RemoveAll
(
cfg
.
StaticDir
)
if
err
!=
nil
{
return
err
}
}
fp
:=
filepath
.
Join
(
md
.
Root
,
cfg
.
PathScope
)
err
=
filepath
.
Walk
(
fp
,
func
(
path
string
,
info
os
.
FileInfo
,
err
error
)
error
{
for
_
,
ext
:=
range
cfg
.
Extensions
{
if
!
info
.
IsDir
()
&&
strings
.
HasSuffix
(
info
.
Name
(),
ext
)
{
// Load the file
body
,
err
:=
ioutil
.
ReadFile
(
path
)
if
err
!=
nil
{
return
err
}
// Get the relative path as if it were a HTTP request,
// then prepend with "/" (like a real HTTP request)
reqPath
,
err
:=
filepath
.
Rel
(
md
.
Root
,
path
)
if
err
!=
nil
{
return
err
}
reqPath
=
"/"
+
reqPath
// Generate the static file
ctx
:=
middleware
.
Context
{
Root
:
md
.
FileSys
}
_
,
err
=
md
.
Process
(
*
cfg
,
reqPath
,
body
,
ctx
)
if
err
!=
nil
{
return
err
}
break
// don't try other file extensions
}
}
return
nil
})
if
err
!=
nil
{
return
err
}
}
}
return
nil
return
nil
...
...
middleware/markdown/generator.go
0 → 100644
View file @
236c8c4e
package
markdown
import
(
"crypto/sha1"
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
"github.com/mholt/caddy/middleware"
)
// GenerateStatic generate static files from markdowns.
func
GenerateStatic
(
md
Markdown
,
cfg
*
Config
)
error
{
generated
,
err
:=
generateLinks
(
md
,
cfg
)
if
err
!=
nil
{
return
err
}
// No new file changes, return.
if
!
generated
{
return
nil
}
// If static site generation is enabled.
if
cfg
.
StaticDir
!=
""
{
if
err
:=
generateStaticHTML
(
md
,
cfg
);
err
!=
nil
{
return
err
}
}
return
nil
}
type
linkGenerator
struct
{
gens
map
[
*
Config
]
*
linkGen
sync
.
Mutex
}
var
generator
=
linkGenerator
{
gens
:
make
(
map
[
*
Config
]
*
linkGen
)}
// generateLinks generates links to all markdown files ordered by newest date.
// This blocks until link generation is done. When called by multiple goroutines,
// the first caller starts the generation and others only wait.
// It returns if generation is done and any error that occurred.
func
generateLinks
(
md
Markdown
,
cfg
*
Config
)
(
bool
,
error
)
{
generator
.
Lock
()
// if link generator exists for config and running, wait.
if
g
,
ok
:=
generator
.
gens
[
cfg
];
ok
{
if
g
.
started
()
{
g
.
addWaiter
()
generator
.
Unlock
()
g
.
Wait
()
// another goroutine has done the generation.
return
false
,
g
.
lastErr
}
}
g
:=
&
linkGen
{}
generator
.
gens
[
cfg
]
=
g
generator
.
Unlock
()
generated
:=
g
.
generateLinks
(
md
,
cfg
)
g
.
discardWaiters
()
return
generated
,
g
.
lastErr
}
// generateStaticFiles generates static html files from markdowns.
func
generateStaticHTML
(
md
Markdown
,
cfg
*
Config
)
error
{
// If generated site already exists, clear it out
_
,
err
:=
os
.
Stat
(
cfg
.
StaticDir
)
if
err
==
nil
{
err
:=
os
.
RemoveAll
(
cfg
.
StaticDir
)
if
err
!=
nil
{
return
err
}
}
fp
:=
filepath
.
Join
(
md
.
Root
,
cfg
.
PathScope
)
return
filepath
.
Walk
(
fp
,
func
(
path
string
,
info
os
.
FileInfo
,
err
error
)
error
{
for
_
,
ext
:=
range
cfg
.
Extensions
{
if
!
info
.
IsDir
()
&&
strings
.
HasSuffix
(
info
.
Name
(),
ext
)
{
// Load the file
body
,
err
:=
ioutil
.
ReadFile
(
path
)
if
err
!=
nil
{
return
err
}
// Get the relative path as if it were a HTTP request,
// then prepend with "/" (like a real HTTP request)
reqPath
,
err
:=
filepath
.
Rel
(
md
.
Root
,
path
)
if
err
!=
nil
{
return
err
}
reqPath
=
"/"
+
reqPath
// Generate the static file
ctx
:=
middleware
.
Context
{
Root
:
md
.
FileSys
}
_
,
err
=
md
.
Process
(
*
cfg
,
reqPath
,
body
,
ctx
)
if
err
!=
nil
{
return
err
}
break
// don't try other file extensions
}
}
return
nil
})
}
// computeDirHash computes an hash on static directory of c.
func
computeDirHash
(
md
Markdown
,
c
Config
)
(
string
,
error
)
{
dir
:=
filepath
.
Join
(
md
.
Root
,
c
.
PathScope
)
if
_
,
err
:=
os
.
Stat
(
dir
);
err
!=
nil
{
return
""
,
err
}
hashString
:=
""
err
:=
filepath
.
Walk
(
dir
,
func
(
path
string
,
info
os
.
FileInfo
,
err
error
)
error
{
if
!
info
.
IsDir
()
&&
c
.
IsValidExt
(
filepath
.
Ext
(
path
))
{
hashString
+=
fmt
.
Sprintf
(
"%v%v%v%v"
,
info
.
ModTime
(),
info
.
Name
(),
info
.
Size
(),
path
)
}
return
nil
})
if
err
!=
nil
{
return
""
,
err
}
sum
:=
sha1
.
Sum
([]
byte
(
hashString
))
return
hex
.
EncodeToString
(
sum
[
:
]),
nil
}
middleware/markdown/markdown.go
View file @
236c8c4e
...
@@ -122,7 +122,7 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
...
@@ -122,7 +122,7 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error
// if development is set, scan directory for file changes for links.
// if development is set, scan directory for file changes for links.
if
m
.
Development
{
if
m
.
Development
{
if
err
:=
Generate
Links
(
md
,
m
);
err
!=
nil
{
if
err
:=
Generate
Static
(
md
,
m
);
err
!=
nil
{
log
.
Println
(
err
)
log
.
Println
(
err
)
}
}
}
}
...
...
middleware/markdown/markdown_test.go
View file @
236c8c4e
...
@@ -68,6 +68,14 @@ func TestMarkdown(t *testing.T) {
...
@@ -68,6 +68,14 @@ func TestMarkdown(t *testing.T) {
}),
}),
}
}
for
i
:=
range
md
.
Configs
{
c
:=
&
md
.
Configs
[
i
]
if
err
:=
GenerateStatic
(
md
,
c
);
err
!=
nil
{
t
.
Fatalf
(
"Error: %v"
,
err
)
}
Watch
(
md
,
c
,
time
.
Millisecond
*
100
)
}
req
,
err
:=
http
.
NewRequest
(
"GET"
,
"/blog/test.md"
,
nil
)
req
,
err
:=
http
.
NewRequest
(
"GET"
,
"/blog/test.md"
,
nil
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"Could not create HTTP request: %v"
,
err
)
t
.
Fatalf
(
"Could not create HTTP request: %v"
,
err
)
...
@@ -157,6 +165,7 @@ func getTrue() bool {
...
@@ -157,6 +165,7 @@ func getTrue() bool {
err
=
os
.
Chtimes
(
"testdata/og/first.md"
,
currenttime
,
currenttime
)
err
=
os
.
Chtimes
(
"testdata/og/first.md"
,
currenttime
,
currenttime
)
currenttime
=
time
.
Now
()
.
Local
()
currenttime
=
time
.
Now
()
.
Local
()
err
=
os
.
Chtimes
(
"testdata/og_static/og/first.md/index.html"
,
currenttime
,
currenttime
)
err
=
os
.
Chtimes
(
"testdata/og_static/og/first.md/index.html"
,
currenttime
,
currenttime
)
time
.
Sleep
(
time
.
Millisecond
*
200
)
md
.
ServeHTTP
(
rec
,
req
)
md
.
ServeHTTP
(
rec
,
req
)
if
rec
.
Code
!=
http
.
StatusOK
{
if
rec
.
Code
!=
http
.
StatusOK
{
...
@@ -169,8 +178,9 @@ func getTrue() bool {
...
@@ -169,8 +178,9 @@ func getTrue() bool {
<title>first_post</title>
<title>first_post</title>
</head>
</head>
<body>
<body>
<h1>Header
title
</h1>
<h1>Header</h1>
Welcome to title!
<h1>Test h1</h1>
<h1>Test h1</h1>
</body>
</body>
...
@@ -185,13 +195,6 @@ func getTrue() bool {
...
@@ -185,13 +195,6 @@ func getTrue() bool {
"/log/test.md"
,
"/log/test.md"
,
}
}
for
i
:=
range
md
.
Configs
{
c
:=
&
md
.
Configs
[
i
]
if
err
:=
GenerateLinks
(
md
,
c
);
err
!=
nil
{
t
.
Fatalf
(
"Error: %v"
,
err
)
}
}
for
i
,
c
:=
range
md
.
Configs
[
:
2
]
{
for
i
,
c
:=
range
md
.
Configs
[
:
2
]
{
log
.
Printf
(
"Test number: %d, configuration links: %v, config: %v"
,
i
,
c
.
Links
,
c
)
log
.
Printf
(
"Test number: %d, configuration links: %v, config: %v"
,
i
,
c
.
Links
,
c
)
if
c
.
Links
[
0
]
.
URL
!=
expectedLinks
[
i
]
{
if
c
.
Links
[
0
]
.
URL
!=
expectedLinks
[
i
]
{
...
@@ -218,7 +221,7 @@ func getTrue() bool {
...
@@ -218,7 +221,7 @@ func getTrue() bool {
w
.
Wait
()
w
.
Wait
()
f
=
func
()
{
f
=
func
()
{
Generate
Links
(
md
,
&
md
.
Configs
[
0
])
Generate
Static
(
md
,
&
md
.
Configs
[
0
])
w
.
Done
()
w
.
Done
()
}
}
for
i
:=
0
;
i
<
5
;
i
++
{
for
i
:=
0
;
i
<
5
;
i
++
{
...
...
middleware/markdown/page.go
View file @
236c8c4e
...
@@ -2,9 +2,6 @@ package markdown
...
@@ -2,9 +2,6 @@ package markdown
import
(
import
(
"bytes"
"bytes"
"crypto/sha1"
"encoding/hex"
"fmt"
"io/ioutil"
"io/ioutil"
"log"
"log"
"os"
"os"
...
@@ -67,7 +64,9 @@ func (l *linkGen) started() bool {
...
@@ -67,7 +64,9 @@ func (l *linkGen) started() bool {
return
l
.
generating
return
l
.
generating
}
}
func
(
l
*
linkGen
)
generateLinks
(
md
Markdown
,
cfg
*
Config
)
{
// generateLinks generate links to markdown files if there are file changes.
// It returns true when generation is done and false otherwise.
func
(
l
*
linkGen
)
generateLinks
(
md
Markdown
,
cfg
*
Config
)
bool
{
l
.
Lock
()
l
.
Lock
()
l
.
generating
=
true
l
.
generating
=
true
l
.
Unlock
()
l
.
Unlock
()
...
@@ -81,7 +80,7 @@ func (l *linkGen) generateLinks(md Markdown, cfg *Config) {
...
@@ -81,7 +80,7 @@ func (l *linkGen) generateLinks(md Markdown, cfg *Config) {
l
.
lastErr
=
err
l
.
lastErr
=
err
l
.
generating
=
false
l
.
generating
=
false
l
.
Unlock
()
l
.
Unlock
()
return
return
false
}
}
hash
,
err
:=
computeDirHash
(
md
,
*
cfg
)
hash
,
err
:=
computeDirHash
(
md
,
*
cfg
)
...
@@ -91,7 +90,7 @@ func (l *linkGen) generateLinks(md Markdown, cfg *Config) {
...
@@ -91,7 +90,7 @@ func (l *linkGen) generateLinks(md Markdown, cfg *Config) {
l
.
Lock
()
l
.
Lock
()
l
.
generating
=
false
l
.
generating
=
false
l
.
Unlock
()
l
.
Unlock
()
return
return
false
}
else
if
err
!=
nil
{
}
else
if
err
!=
nil
{
log
.
Println
(
"Error:"
,
err
)
log
.
Println
(
"Error:"
,
err
)
}
}
...
@@ -162,58 +161,5 @@ func (l *linkGen) generateLinks(md Markdown, cfg *Config) {
...
@@ -162,58 +161,5 @@ func (l *linkGen) generateLinks(md Markdown, cfg *Config) {
l
.
Lock
()
l
.
Lock
()
l
.
generating
=
false
l
.
generating
=
false
l
.
Unlock
()
l
.
Unlock
()
}
return
true
type
linkGenerator
struct
{
gens
map
[
*
Config
]
*
linkGen
sync
.
Mutex
}
var
generator
=
linkGenerator
{
gens
:
make
(
map
[
*
Config
]
*
linkGen
)}
// GenerateLinks generates links to all markdown files ordered by newest date.
// This blocks until link generation is done. When called by multiple goroutines,
// the first caller starts the generation and others only wait.
func
GenerateLinks
(
md
Markdown
,
cfg
*
Config
)
error
{
generator
.
Lock
()
// if link generator exists for config and running, wait.
if
g
,
ok
:=
generator
.
gens
[
cfg
];
ok
{
if
g
.
started
()
{
g
.
addWaiter
()
generator
.
Unlock
()
g
.
Wait
()
return
g
.
lastErr
}
}
g
:=
&
linkGen
{}
generator
.
gens
[
cfg
]
=
g
generator
.
Unlock
()
g
.
generateLinks
(
md
,
cfg
)
g
.
discardWaiters
()
return
g
.
lastErr
}
// computeDirHash computes an hash on static directory of c.
func
computeDirHash
(
md
Markdown
,
c
Config
)
(
string
,
error
)
{
dir
:=
filepath
.
Join
(
md
.
Root
,
c
.
PathScope
)
if
_
,
err
:=
os
.
Stat
(
dir
);
err
!=
nil
{
return
""
,
err
}
hashString
:=
""
err
:=
filepath
.
Walk
(
dir
,
func
(
path
string
,
info
os
.
FileInfo
,
err
error
)
error
{
if
!
info
.
IsDir
()
&&
c
.
IsValidExt
(
filepath
.
Ext
(
path
))
{
hashString
+=
fmt
.
Sprintf
(
"%v%v%v%v"
,
info
.
ModTime
(),
info
.
Name
(),
info
.
Size
(),
path
)
}
return
nil
})
if
err
!=
nil
{
return
""
,
err
}
sum
:=
sha1
.
Sum
([]
byte
(
hashString
))
return
hex
.
EncodeToString
(
sum
[
:
]),
nil
}
}
middleware/markdown/testdata/og_static/og/first.md/index.html
deleted
100644 → 0
View file @
96693635
<!DOCTYPE html>
<html>
<head>
<title>
first_post
</title>
</head>
<body>
<h1>
Header title
</h1>
<h1>
Test h1
</h1>
</body>
</html>
\ No newline at end of file
middleware/markdown/watcher.go
View file @
236c8c4e
package
markdown
package
markdown
import
"time"
import
(
"log"
"time"
)
const
DefaultInterval
=
time
.
Second
*
60
const
DefaultInterval
=
time
.
Second
*
60
...
@@ -8,12 +11,14 @@ const DefaultInterval = time.Second * 60
...
@@ -8,12 +11,14 @@ const DefaultInterval = time.Second * 60
// when there are changes.
// when there are changes.
func
Watch
(
md
Markdown
,
c
*
Config
,
interval
time
.
Duration
)
(
stopChan
chan
struct
{})
{
func
Watch
(
md
Markdown
,
c
*
Config
,
interval
time
.
Duration
)
(
stopChan
chan
struct
{})
{
return
TickerFunc
(
interval
,
func
()
{
return
TickerFunc
(
interval
,
func
()
{
GenerateLinks
(
md
,
c
)
if
err
:=
GenerateStatic
(
md
,
c
);
err
!=
nil
{
log
.
Println
(
err
)
}
})
})
}
}
// TickerFunc runs f at interval.
If interval is <= 0, it loops f. A message to
the
// TickerFunc runs f at interval.
A message to the returned channel will stop
the
//
returned channel will stop the
executing goroutine.
// executing goroutine.
func
TickerFunc
(
interval
time
.
Duration
,
f
func
())
chan
struct
{}
{
func
TickerFunc
(
interval
time
.
Duration
,
f
func
())
chan
struct
{}
{
stopChan
:=
make
(
chan
struct
{})
stopChan
:=
make
(
chan
struct
{})
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment