Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 30 additions & 14 deletions .github/workflows/static.yml → .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
# Simple workflow for deploying static content to GitHub Pages
name: Deploy static content to Pages
# GitHub Actions workflow for deploying website to GitHub Pages
#
# References:
#
# * Workflow syntax for GitHub Actions - GitHub Docs
# https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions
#
# Copyright 2025 林博仁(Buo-ren Lin) <[email protected]>
# SPDX-License-Identifier: CC-BY-SA-4.0
name: Deploy site to GitHub Pages

on:
# Runs on pushes targeting the default branch
push:
branches: ["gh-pages"]
branches:
- production

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
Expand All @@ -18,26 +27,33 @@ permissions:
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
group: pages
cancel-in-progress: false

jobs:
# Single deploy job since we're just deploying
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Build with Grunt
# Outputs to the './_site' directory by default
run: |
sudo ./dev-utils/deploy-dev-env.sh
grunt dist
- name: Upload artifact
# Automatically uploads an artifact from the './_site' directory by default
uses: actions/upload-pages-artifact@v3
with:
# Upload entire repository
path: '.'

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ node_modules

.DS_Store
Thumbs.db

# Don't track the built site files'
/_site/
37 changes: 35 additions & 2 deletions Gruntfile.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
grunt.loadNpmTasks('grunt-contrib-clean');

var BSconfig = require('./bs-config');
grunt.initConfig({
watch: {
Expand Down Expand Up @@ -35,8 +37,31 @@ module.exports = function(grunt) {
files: [{
expand: true,
src: ['**/*.shtml', '!node_modules/**/*.shtml'],
dest: './',
ext: '.html'
}]
},
dist: {
files: [
{
src: [
'**/*',
'!**/*.shtml',
'!**/*.php',
'!bs-config.js',
'!docker-*',
'!package*.json',
'!Gruntfile.js',
'!gulpfile.js',
'!inc/**',
'!node_modules/**',
'!_site/**',
'!Vagrantfile'
],
dest: './_site/',
filter: 'isFile'
}
]
}
},
ssi: {
Expand All @@ -60,7 +85,10 @@ module.exports = function(grunt) {
homepage: "http://moztw.org/",
changefreq: "monthly",
}
}
},
clean: {
dist: ['_site/']
}
});
grunt.event.on('watch', function(action, filepath) {
var cfgkey = ['copy', 'main', 'files'];
Expand All @@ -83,5 +111,10 @@ module.exports = function(grunt) {
grunt.registerTask('default', ['browserSync']);

// 新增 build 任務
grunt.registerTask('build', ['copy', 'ssi']);
grunt.registerTask('build', ['clean:dist', 'copy:main', 'ssi']);

// Build and install site files to the _site/ distribution directory.
// FIXME: Subsequent builds will fail if the _site/ directory isn't removed.
// FIXME: Test environment container that bind-mounts the _site directory will not work if it is created _before_ the dist operation(as it will be removed during the process).
grunt.registerTask('dist', ['clean:dist', 'copy:main', 'ssi', 'copy:dist']);
};
3 changes: 3 additions & 0 deletions dev-utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# dev-utils

Utilities for assisting project development.
224 changes: 224 additions & 0 deletions dev-utils/deploy-dev-env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
#!/usr/bin/env bash
# Deploy environment for developing the project
#
# Copyright 2025 林博仁(Buo-ren Lin) <[email protected]>
# SPDX-License-Identifier: MPL-2.0

set_opts=(
# Terminate script execution when an unhandled error occurs
-o errexit
-o errtrace

# Terminate script execution when an unset parameter variable is
# referenced
-o nounset
)
if ! set "${set_opts[@]}"; then
printf \
'Error: Unable to configure the defensive interpreter behaviors.\n' \
1>&2
exit 1
fi

required_commands=(
apt-get
dpkg
realpath
)
flag_required_command_check_failed=false
for command in "${required_commands[@]}"; do
if ! command -v "${command}" >/dev/null; then
flag_required_command_check_failed=true
printf \
'Error: This program requires the "%s" command to be available in your command search PATHs.\n' \
"${command}" \
1>&2
fi
done
if test "${flag_required_command_check_failed}" == true; then
printf \
'Error: Required command check failed, please check your installation.\n' \
1>&2
exit 1
fi

if test -v BASH_SOURCE; then
# Convenience variables may not need to be referenced
# shellcheck disable=SC2034
{
printf \
'Info: Determining the absolute path of the program...\n'
if ! script="$(realpath "${BASH_SOURCE[0]}")"; then
printf \
'Error: Unable to determine the absolute path of the program.\n' \
1>&2
exit 1
fi
script_dir="${script%/*}"
script_filename="${script##*/}"
script_name="${script_filename%%.*}"
}
fi
# Convenience variables may not need to be referenced
# shellcheck disable=SC2034
{
script_args=("${@}")
}

trap_err(){
printf -- \
'%s: Error: The program prematurely terminated due to an unhandled error.\n' \
"${script_name}" \
1>&2
exit 99
}
if ! trap trap_err ERR; then
printf -- \
'%s: Error: Unable to set the ERR trap.\n' \
"${script_name}" \
1>&2
exit 1
fi

if test "${EUID}" -ne 0; then
printf -- \
'%s: Error: This program is required to be run as the superuser(root).\n' \
"${script_name}" \
1>&2
exit 1
fi

if test "${#script_args[@]}" -ne 0; then
printf -- \
'%s: Error: This program does not accept any command-line arguments.\n' \
"${script_name}" \
1>&2
exit 2
fi

if ! current_epoch_time="$(printf '%(%s)T')"; then
printf -- \
'%s: Error: Unable to query the current epoch time.\n' \
"${script_name}" \
1>&2
exit 2
fi

if ! apt_list_modification_time="$(stat --format=%Y /var/lib/apt/lists)"; then
printf -- \
'%s: Error: Unable to query the modification of the local cache of the APT software sources.\n' \
"${script_name}" \
1>&2
exit 2
fi

if test "$((current_epoch_time - apt_list_modification_time))" -ge 86400; then
printf -- \
'%s: Info: Syncing the software source cache of the APT package manager...\n' \
"${script_name}"
if ! apt-get update; then
printf -- \
'%s: Error: Unable to sync the software source cache of the APT package manager.\n' \
"${script_name}" \
1>&2
exit 2
fi
fi

# Avoid debconf interactive prompts on Debian-like systems
export DEBIAN_FRONTEND=noninteractive

dev_deps_pkgs=(
grunt
npm
)
if ! dpkg --status "${dev_deps_pkgs[@]}" &>/dev/null; then
printf -- \
'%s: Info: Installing the project development dependencies packages...\n' \
"${script_name}"
aptget_install_opts=(
--no-install-recommends
--yes
)
if ! apt-get install "${aptget_install_opts[@]}" \
"${dev_deps_pkgs[@]}"; then
printf -- \
'%s: Error: Unable to install the project development dependencies packages.\n' \
"${script_name}" \
1>&2
exit 2
fi
fi

if test "${script_dir}" == /etc/profile.d; then
# We're in the development environment container
project_dir=/project
else
if ! project_dir="$(realpath "${script_dir}/..")"; then
printf -- \
'%s: Error: Unable to determine the absolute path of the project directory.\n' \
"${script_name}" \
1>&2
exit 2
fi
fi

if ! cd "${project_dir}"; then
printf -- \
'%s: Error: Unable to switch the working directory to the project directory.\n' \
"${script_name}" \
1>&2
exit 2
fi

printf -- \
'%s: Info: Installing the Node.js dependencies of the project...\n' \
"${script_name}"
if test -v SUDO_USER; then
if ! sudo -u "${SUDO_USER}" npm install; then
printf -- \
'%s: Error: Unable to install the Node.js dependencies of the project.\n' \
"${script_name}" \
1>&2
exit 2
fi
else
if ! npm install; then
printf -- \
'%s: Error: Unable to install the Node.js dependencies of the project.\n' \
"${script_name}" \
1>&2
exit 2
fi
fi

printf -- \
'%s: Info: Operation completed without errors.\n' \
"${script_name}"

if test "${#BASH_SOURCE[@]}" -gt 1; then
set_opts=(
+o errexit
+o errtrace
+o nounset
)
if ! set "${set_opts[@]}"; then
printf -- \
'%s: Error: Unable to reset the interpreter behaviors.\n' \
"${script_name}" \
1>&2
return 2
fi

if ! trap - ERR; then
printf -- \
'%s: Error: Unable to reset the ERR trap.\n' \
"${script_name}" \
1>&2
return 2
fi

return 0
else
exit 0
fi
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"connect-modrewrite": "~0.8.2",
"grunt": "~0.4.5",
"grunt-browser-sync": "^2.2.0",
"grunt-contrib-clean": "^2.0.1",
"grunt-contrib-copy": "^0.8.2",
"grunt-contrib-watch": "~0.6.1",
"grunt-sitemap": "^1.2.1",
Expand Down