Initial commit
This commit is contained in:
commit
f2f978eaab
58
README.md
Normal file
58
README.md
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# dg-x
|
||||||
|
|
||||||
|
Static site build system for [datagirl.xyz](https://datagirl.xyz).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
build.pl uses the following modules:
|
||||||
|
|
||||||
|
* Cwd
|
||||||
|
* File::Copy::Recursive
|
||||||
|
* File::Path
|
||||||
|
* Markdent
|
||||||
|
* Text::Template
|
||||||
|
|
||||||
|
ptouch.pl uses the following modules:
|
||||||
|
|
||||||
|
* Tie::File
|
||||||
|
* Getopt::Std
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
You'll need to create the following directories:
|
||||||
|
|
||||||
|
* posts/
|
||||||
|
* pages/ - Templated pages
|
||||||
|
* assets/ - Static assets
|
||||||
|
* templates/ - Templates you can call with `include_tmpl`
|
||||||
|
|
||||||
|
To make a new post, create a file with the following format in the
|
||||||
|
posts/ folder:
|
||||||
|
|
||||||
|
```
|
||||||
|
title=Your blog title
|
||||||
|
desc=[Not yet implemented] Description for the RSS feed
|
||||||
|
---
|
||||||
|
This is some content.
|
||||||
|
|
||||||
|
## About this content
|
||||||
|
Markdown is supported.
|
||||||
|
```
|
||||||
|
|
||||||
|
There is no set schema for tags, so you can put whatever you want as
|
||||||
|
a tag. However, it may not be used by the build script.
|
||||||
|
|
||||||
|
Once you're happy with your post, use the ptouch script to set the
|
||||||
|
created tag:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./scripts/ptouch.pl -n posts/your_new_post
|
||||||
|
```
|
||||||
|
|
||||||
|
And then compile the site:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./scripts/build.pl
|
||||||
|
```
|
||||||
|
|
||||||
|
The site can then be pushed with rsync, sftp, netcat, etc.
|
190
scripts/build.pl
Executable file
190
scripts/build.pl
Executable file
|
@ -0,0 +1,190 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use Cwd qw(getcwd);
|
||||||
|
use File::Basename qw(dirname basename);
|
||||||
|
use File::Copy::Recursive qw(dircopy);
|
||||||
|
use File::Find qw(finddepth);
|
||||||
|
use File::Path qw(make_path);
|
||||||
|
use Markdent::Simple::Fragment;
|
||||||
|
use Text::Template;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
use constant {
|
||||||
|
OUT_PATH => "/out",
|
||||||
|
POSTS_PATH => "/posts",
|
||||||
|
ASSETS_PATH => "/assets",
|
||||||
|
PAGES_PATH => "/pages",
|
||||||
|
TEMPLATES_PATH => "/templates",
|
||||||
|
};
|
||||||
|
|
||||||
|
# globals
|
||||||
|
my $cwd = getcwd;
|
||||||
|
my $post_dir = $cwd . POSTS_PATH;
|
||||||
|
my $pages_dir = $cwd . PAGES_PATH;
|
||||||
|
my $tmpl_dir = $cwd . TEMPLATES_PATH;
|
||||||
|
my $post_tmpl = Text::Template->new(SOURCE => "$tmpl_dir/post.html.tmpl");
|
||||||
|
my $assets_path = $cwd . ASSETS_PATH;
|
||||||
|
my $out_path = $cwd . OUT_PATH;
|
||||||
|
my $postout_path = $out_path . POSTS_PATH;
|
||||||
|
|
||||||
|
# Converts a post file to a metadata hash.
|
||||||
|
#
|
||||||
|
# Takes one argument, the path to the post file.
|
||||||
|
#
|
||||||
|
# Returns a hash with the relevant metadata (body text is stored as
|
||||||
|
# $metadata{"content"}, desired filename is stored as
|
||||||
|
# $metadata{"fname"}).
|
||||||
|
sub post_to_meta {
|
||||||
|
defined(my $fname = shift) or warn "No filename argument!";
|
||||||
|
my %metadata;
|
||||||
|
|
||||||
|
open(MDIN, '<', $fname) or die "Unable to open $fname: " . $!;
|
||||||
|
while (<MDIN>) {
|
||||||
|
chomp;
|
||||||
|
if (/^(.+)?=(.*)$/) {
|
||||||
|
$metadata{$1} = $2;
|
||||||
|
} elsif (/^---$/) {
|
||||||
|
last;
|
||||||
|
} else {
|
||||||
|
warn basename($fname) . ":" . $. . ": malformed line; ignored"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $body = do { local $/; <MDIN> };
|
||||||
|
close(MDIN);
|
||||||
|
|
||||||
|
# HACK: Stuffing the basename in the metadata because I don't want
|
||||||
|
# to deal with hashes of hashes
|
||||||
|
$metadata{fname} = basename($fname) unless exists($metadata{"fname"});
|
||||||
|
if ($body ne "") {
|
||||||
|
my $parser = Markdent::Simple::Fragment->new;
|
||||||
|
|
||||||
|
$metadata{content} = $parser->markdown_to_html(
|
||||||
|
dialects => 'GitHub',
|
||||||
|
markdown => $body
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
%metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Gets an array of all posts for a directory.
|
||||||
|
#
|
||||||
|
# Takes one argument, the path to the posts directory.
|
||||||
|
#
|
||||||
|
# Returns an array of metadata hashes (see post_to_meta above for
|
||||||
|
# more on what those hashes look like).
|
||||||
|
sub all_posts_for_dir {
|
||||||
|
defined(my $postdir = shift) or warn "No directory argument!";
|
||||||
|
my @posts;
|
||||||
|
|
||||||
|
opendir(PD, $postdir) or die $!;
|
||||||
|
while (my $fname = readdir(PD)) {
|
||||||
|
next if ($fname =~ /^\.+$/);
|
||||||
|
my %post = &post_to_meta("$postdir/$fname");
|
||||||
|
push @posts, \%post;
|
||||||
|
}
|
||||||
|
closedir(PD);
|
||||||
|
|
||||||
|
sort { $b->{created} cmp $a->{created} } @posts;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub all_pages_for_dir {
|
||||||
|
defined(my $pagedir = shift) or warn "No directory argument!";
|
||||||
|
my @pages;
|
||||||
|
|
||||||
|
finddepth(sub {
|
||||||
|
return if ($File::Find::name =~ /^\.+$/);
|
||||||
|
my %page;
|
||||||
|
$_ = $File::Find::name;
|
||||||
|
if (/^$pagedir\/(.+).tmpl$/) {
|
||||||
|
$page{fname} = $1;
|
||||||
|
$page{tmpl} = Text::Template->new(SOURCE => $File::Find::name);
|
||||||
|
push @pages, \%page;
|
||||||
|
}
|
||||||
|
}, $pagedir);
|
||||||
|
|
||||||
|
@pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub mkpath {
|
||||||
|
make_path( shift, { error => \my $path_err } );
|
||||||
|
if ($path_err && @$path_err) {
|
||||||
|
print "Errors occurred while making posts directory!\n";
|
||||||
|
foreach my $err (@$path_err) {
|
||||||
|
my ($path, $msg) = %$err;
|
||||||
|
if ($path eq '') {
|
||||||
|
print " $msg\n";
|
||||||
|
} else {
|
||||||
|
print " $path: $msg\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# effectively die
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub include_tmpl {
|
||||||
|
my $tmpl_name = shift;
|
||||||
|
# TODO: do we want to cache snippet-type templates like this?
|
||||||
|
my $tmpl = Text::Template->new(SOURCE => "$tmpl_dir/$tmpl_name.tmpl");
|
||||||
|
if (defined $tmpl) {
|
||||||
|
my $props = shift;
|
||||||
|
$tmpl->fill_in(HASH => { props => \$props });
|
||||||
|
} else {
|
||||||
|
"error: $tmpl_dir/$tmpl_name.tmpl is missing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
## End subroutines
|
||||||
|
|
||||||
|
|
||||||
|
# make posts dir or die
|
||||||
|
mkpath($postout_path) or die "Unable to create directory.";
|
||||||
|
|
||||||
|
my @posts = all_posts_for_dir $post_dir;
|
||||||
|
print "Generating blog posts...\n";
|
||||||
|
foreach my $post (@posts) {
|
||||||
|
if (!exists $post->{content}) {
|
||||||
|
print " $post->{fname} has no content, not making a page...\n";
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
print " Processing $post->{fname}...\n";
|
||||||
|
my $post_content = $post_tmpl->fill_in(HASH => { post => \$post, include_tmpl => \&include_tmpl });
|
||||||
|
if (defined($post_content)) {
|
||||||
|
open(POSTOUT, '>', "$postout_path/$post->{fname}.html") or die("Unable to write $post->{fname}.html: $!");
|
||||||
|
print POSTOUT $post_content;
|
||||||
|
close(POSTOUT);
|
||||||
|
} else {
|
||||||
|
die "Failed to process $post->{fname}: $Text::Template::ERROR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Generating pages...\n";
|
||||||
|
my @pages = all_pages_for_dir $pages_dir;
|
||||||
|
foreach my $pg (@pages) {
|
||||||
|
mkpath(dirname("$out_path/" . $pg->{fname})) or die "Unable to create directory.";
|
||||||
|
print " Processing $pg->{fname}...\n";
|
||||||
|
my $page_content = $pg->{tmpl}->fill_in(HASH => {
|
||||||
|
posts => \@posts,
|
||||||
|
include_tmpl => \&include_tmpl,
|
||||||
|
});
|
||||||
|
if (defined($page_content)) {
|
||||||
|
open(PGOUT, '>', "$out_path/$pg->{fname}") or die("Unable to write $pg->{fname}: $!");
|
||||||
|
print PGOUT $page_content;
|
||||||
|
close(PGOUT);
|
||||||
|
} else {
|
||||||
|
die "Failed to process $pg->{fname}: $Text::Template::ERROR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Copying static assets...\n";
|
||||||
|
local $File::Copy::Recursive::RMTrgDir = 2;
|
||||||
|
dircopy($assets_path, "$out_path/" . ASSETS_PATH) or die "Unable to copy assets: $!";
|
||||||
|
|
||||||
|
print "Done! Artifacts have been stored in $out_path.\n";
|
47
scripts/ptouch.pl
Executable file
47
scripts/ptouch.pl
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use utf8;
|
||||||
|
use Getopt::Std;
|
||||||
|
use POSIX qw(strftime);
|
||||||
|
use Tie::File;
|
||||||
|
|
||||||
|
$::VERSION = "1.0.0";
|
||||||
|
|
||||||
|
getopts("ne", \my %opts);
|
||||||
|
|
||||||
|
my $metavar;
|
||||||
|
if (defined $opts{n}) {
|
||||||
|
# "new" post
|
||||||
|
$metavar = "created";
|
||||||
|
} elsif (defined $opts{e}) {
|
||||||
|
# "edited" post
|
||||||
|
$metavar = "modified";
|
||||||
|
} else {
|
||||||
|
die "Neither -n[ew] nor -e[dited] are defined!";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $pretty_date = strftime "%Y-%m-%d %H:%M", localtime;
|
||||||
|
|
||||||
|
my $fname = shift @ARGV;
|
||||||
|
tie my @fharr, 'Tie::File', $fname or die $!;
|
||||||
|
|
||||||
|
my $done = 0;
|
||||||
|
for (@fharr) {
|
||||||
|
if (s/^$metavar=.+/$metavar=$pretty_date/) {
|
||||||
|
print "$fname:$.: Overwriting existing \`$metavar\'\n";
|
||||||
|
$done++;
|
||||||
|
}
|
||||||
|
if (s/^(---)$/$metavar=$pretty_date\n$1/) {
|
||||||
|
print "$fname:$.: Inserting new \`$metavar\'\n";
|
||||||
|
$done++;
|
||||||
|
}
|
||||||
|
last if ($done);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$done) {
|
||||||
|
print "$fname: No content and no \`$metavar\', inserting at end\n";
|
||||||
|
push @fharr, "$metavar=$pretty_date";
|
||||||
|
}
|
||||||
|
|
||||||
|
untie @fharr;
|
Loading…
Reference in a new issue