From: Lady Date: Sun, 21 Jan 2024 00:35:28 +0000 (-0500) Subject: Initial commit X-Git-Tag: 0.1.0^0 X-Git-Url: https://git.ladys.computer/Yseme/commitdiff_plain/11d9fdbb76bac028c834c84521fcc45c6a95ea6e?ds=inline Initial commit --- 11d9fdbb76bac028c834c84521fcc45c6a95ea6e diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 0000000..8baaf0f --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,77 @@ +SHELL = /bin/sh + +# © 2023–2024 Lady [@ Lady’s Computer]. +# +# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +# If a copy of the MPL was not distributed with this file, You can obtain one at . + +BUILDTARGET := .grass +DESTDIR := . + +CLIENTCHARSET := utf-8-mac +SERVER := computer +SERVERCHARSET := utf-8 +SERVERPATH := $(notdir $(abspath .)) + +AWK := awk +ECHO := echo +SED := sed +TEST := test + +GIT := git +GITFORCE := +GITOPTS := + +# This Makefile requires rsync 3 or newer. +RSYNC := rsync +ifneq ($(wildcard .rsync-filter),) +RSYNCFILTER := .rsync-filter +endif +RSYNCOPTS := --checksum --compress --del --links --omit-dir-times --prune-empty-dirs --recursive --times --verbose + +override comma := , + +ifneq ($(wildcard $(BUILDTARGET)),) +override buildtime := $(shell stat -f '%m' $(BUILDTARGET)) +endif + +override committime := $(shell $(GIT) $(GITOPTS) log -1 --format='%ct' | $(AWK) '{print $$NF}' || true) +ifneq ($(committime),) +override gitstatus := $(shell $(GIT) $(GITOPTS) status --porcelain) +endif + +ensure-build: +ifeq ($(committime),) + @$(ECHO) 'Error: Unable to get commit time of most recent commit!' >&2 + @false +endif +ifeq ($(buildtime),) + @$(ECHO) 'Error: The website has not been built yet!' >&2 + @$(ECHO) 'Run `make´ before syncing.' >&2 + @false +endif + @if $(TEST) '$(committime)' -gt '$(buildtime)'; then $(ECHO) 'Error: A commit was made after the last build!' >&2; $(ECHO) 'Run `make´ before syncing.' >&2; false; fi + +ensure-branch-up-to-date: +ifeq ($(committime),) + @$(ECHO) 'Error: Unable to get commit time of most recent commit!' >&2 + @false +endif + $(GIT)$(if $(GITOPTS), $(GITOPTS),) fetch + @if ! $(GIT) $(GITOPTS) merge-base --is-ancestor @{u} HEAD; then $(ECHO) 'Error: This branch is currently out‐of‐date!' >&2; $(ECHO) 'Pull in changes with `$(GIT)$(if $(GITOPTS), $(GITOPTS),) pull´ before syncing.' >&2; false; fi + +ensure-clean: +ifneq ($(gitstatus),) + @$(ECHO) 'Error: There are uncommitted changes!' >&2 + @$(ECHO) 'Commit changes and run `make´ before syncing.' >&2 + @false +endif + +dry-sync: ensure-clean$(if $(GITFORCE),, ensure-branch-up-to-date) ensure-build + cd $(DESTDIR) && $(RSYNC) --dry-run$(if $(RSYNCFILTER), --filter='. $(abspath $(RSYNCFILTER))',)$(if $(and $(CLIENTCHARSET),$(SERVERCHARSET),$(filter-out $(CLIENTCHARSET),$(SERVERCHARSET))), --iconv='$(CLIENTCHARSET)$(comma)$(SERVERCHARSET)',) $(RSYNCOPTS) . $(SERVER):$(SERVERPATH) + +sync: ensure-clean$(if $(GITFORCE),, ensure-branch-up-to-date) ensure-build + $(GIT)$(if $(GITOPTS), $(GITOPTS),) push$(if $(GITFORCE), --force,) + cd $(DESTDIR) && $(RSYNC)$(if $(RSYNCFILTER), --filter='. $(abspath $(RSYNCFILTER))',)$(if $(and $(CLIENTCHARSET),$(SERVERCHARSET),$(filter-out $(CLIENTCHARSET),$(SERVERCHARSET))), --iconv='$(CLIENTCHARSET)$(comma)$(SERVERCHARSET)',) $(RSYNCOPTS) . $(SERVER):$(SERVERPATH) + +.PHONY: dry-sync ensure-branch-up-to-date ensure-build ensure-clean sync; diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..e093ca5 --- /dev/null +++ b/README.markdown @@ -0,0 +1,158 @@ +# 👥📤 Yseme + +**A make·file for syncing.** + +👥📤 Yseme is an implementation of the sync process described in my + blogpost [C·I pipelines have you down? Why not ‹ touch + .grass ›?!][touch_grass]. +It is intended to be included in projects as a Git submodule, and + recursively called from within another make·file. + +**Note:** +👥📤 Yseme requires functionality present in G·N·U Make 3.81 (or + later) and will not work in previous versions, or other + implementations of Make. +Compatibility with later versions of G·N·U Make is assumed, but not + tested. + +## Nomenclature + +Yseme combines two archaic English terms: y‐ (indicating + togetherness or wholeness) and seme (< Old Norse sǿmr, + meaning “seemly”, “appropriate”, or “beautiful”). + +## Basic Usage + +`make dry-sync` will ensure that the current repository is clean, + built, and up‐to‐date, and then perform a dry run (`--dry-run`) of + R·Sync. +`make sync` will perform the same checks, then push any new commits and + perform an actual run of R·Sync. + +👥📤 Yseme is intended to be called recursively from with·in another + make·file. +The following is a sample make·file which makes use of 👥📤 Yseme :⁠— + +```make +SHELL = /bin/sh + +# © 2024 Lady [@ Lady’s Computer]. +# +# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. +# If a copy of the MPL was not distributed with this file, You can obtain one at . + +# Assume 👥📤 Yseme is a git submodule located at <.yseme> in the +# current directory. +YSEME := .yseme +YSEMEVARS := DESTDIR='public' + +# Build script (substitute with your own). +build: + if [ ! -d public ]; then mkdir public; fi + echo 'Hello, world!' > public/index + touch .grass + +# Set up the 👥📤 Yseme submodule. +$(YSEME)/GNUmakefile: + git submodule update --init $(YSEME) + +# Reload this make·file if the 👥📤 Yseme make·file has changed. +# (Replace `GNUmakefile´ with whatever your make·file is called.) +# +# This is required to ensure the correct value of the `YSEME_TARGETS´ variable. +# +# See . +GNUmakefile: $(YSEME)/GNUmakefile + touch GNUmakefile + +# Ensure the 👥📤 Yseme make·file exists before running the following commands. +ifneq ($(wildcard $(YSEME)/GNUmakefile),) +# Programmatically get the list of phony targets defined by 👥📤 Yseme. +# You can also list these manually (e·g `sync´, `dry-sync´). +YSEME_TARGETS := $(shell sed '/^\.PHONY[ :]/!d;/^\.PHONY[ :]/s/ *;.*//;/^\.PHONY[ :]/s/\.PHONY.*: *//' < $(YSEME)/GNUmakefile) + +# For each target defined by 👥📤 Yseme, forward it appropriately. +$(YSEME_TARGETS): + $(MAKE) -f $(YSEME)/GNUmakefile $@ $(YSEMEVARS) +endif +``` + +👥📤 Yseme was designed and tested with G·N·U Make 3.81. +It likely works with newer versions of Make, but may break in older + versions. + +## Setup and Configuration + +👥📤 Yseme depends on the following programs to run. +In every case, you may supply your own implementation by overriding the + corresponding (allcaps) variable (e·g, set `RSYNC` to supply your own + `rsync` implementation). + +- `awk` +- `echo` +- `git` +- `rsync` (version 3.0 or later) +- `sed` +- `test` + +The following varibales provide general configuration :⁠— + +- **`BUILDTARGET`:** + A file whose timestamp is guaranteed to be updated on every build + (default: `.grass`). + 👥📤 Yseme will compare the modified time of this file to the commit + time of the latest commit to determine if the build is out·of·date. + +- **`DESTDIR`:** + The directory which contains the result of the build. + `rsync` will be called from this directory. + +The following variables configure Git :⁠— + +- **`GITFORCE`:** + If this variable has a value, the current branch does not need to be + up·to·date with its remote counterpart and a force‐push will be + used (default: empty). + +- **`GITOPTS`:** + Options to pass to Git (default: empty). + +The following variables configuer R·Sync :⁠— + +- **`CLIENTCHARSET`:** + The character set of the local machine (default: `utf-8-mac`). + If both this and `SERVERCHARSET` are set and not equal, an appropriate + `--iconv` option will be added to the R·Sync call. + +- **`RSYNCFILTER`:** + The location (relative to the working directory) of an R·Sync filter + file (default: `.rsync-filter` if such a file exists; otherwise, + empty). + +- **`RSYNCOPTS`:** + Option to use when calling R·Sync. + The following flags are set by default :⁠— + `--checksum`, `--compress`, `--del`, `--links`, `--omit-dir-times`, + `--prune-empty-dirs`, `--recursive`, `--times`, `--verbose`. + Additional flags may be set depending on the values of variables and + which target is being built (e·g `dry-sync` 🆚 `sync`). + +- **`SERVER`:** + The remote machine to sync to (default: `computer`). + +- **`SERVERCHARSET`:** + The character set of the remote machine (default: `utf-8`). + If both this and `CLIENTCHARSET` are set and not equal, an appropriate + `--iconv` option will be added to the R·Sync call. + +- **`SERVERPATH`:** + The path on the remote machine to sync to (default: the name of the + current directory). + +If you use a method of syncing your website other than `rsync`, you can + still use 👥📤 Yseme: +Just define your own `sync` and `dry-sync` targets which depend on + 👥📤 Yseme’s `ensure-clean`, `ensure-branch-up-to-date`, and + `ensure-build`. + +[touch_grass]: "C·I pipelines have you down? Why not ‹ touch .grass ›?!"