mirror of
https://github.com/MarlinFirmware/Configurations.git
synced 2025-08-05 13:04:06 -06:00
✅ Add / use deploy script
This commit is contained in:
parent
069572506d
commit
e97fb182f3
2 changed files with 255 additions and 97 deletions
102
.github/workflows/deploy.yml
vendored
102
.github/workflows/deploy.yml
vendored
|
@ -10,6 +10,9 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- import-2.1.x
|
||||
paths:
|
||||
- 'config/**'
|
||||
- 'bin/mfconfig'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
@ -19,101 +22,6 @@ jobs:
|
|||
- name: Check out
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# For each directory containing a changed config file, copy the .h files and build the code:
|
||||
# Run the mfconfig script with CI action
|
||||
- name: Deploy bugfix-2.1.x
|
||||
run: |
|
||||
IMPORT=import-2.1.x
|
||||
EXPORT=bugfix-2.1.x
|
||||
CEXA=config/examples
|
||||
CDEF=config/default
|
||||
BC=Configuration.h
|
||||
AC=Configuration_adv.h
|
||||
|
||||
git config user.email "thinkyhead@users.noreply.github.com"
|
||||
git config user.name "Scott Lahteine"
|
||||
|
||||
echo "- Initializing BASE branch..."
|
||||
|
||||
# Copy to a temporary location
|
||||
TEMP=$( mktemp -d ) ; cp -R config $TEMP
|
||||
|
||||
# Strip all #error lines
|
||||
IFS=$'\n'; set -f
|
||||
for fn in $( find $TEMP/config -type f -name "Configuration.h" ); do
|
||||
sed -i~ -e "20,30{/#error/d}" "$fn"
|
||||
rm "$fn~"
|
||||
done
|
||||
unset IFS; set +f
|
||||
|
||||
# Create 'BASE' as a copy of 'init-repo' (README, LICENSE, etc.)
|
||||
git fetch origin init-repo >/dev/null
|
||||
git checkout origin/init-repo -b BASE >/dev/null || exit
|
||||
|
||||
# Copy all config files into place
|
||||
echo "- Copying configs from fresh $IMPORT..."
|
||||
cp -R "$TEMP/config" . >/dev/null
|
||||
|
||||
echo "- Deleting extras"
|
||||
|
||||
# Delete anything that's not a Configuration file
|
||||
find config -type f \! -name "Conf*.h" -exec rm "{}" \;
|
||||
|
||||
# Init Cartesian/SCARA/TPARA configurations to default
|
||||
echo "- Initializing configs to default state..."
|
||||
|
||||
find "$CEXA" -name $BC -print0 \
|
||||
| while read -d $'\0' F ; do cp "$CDEF/$BC" "$F" >/dev/null ; done
|
||||
find "$CEXA" -name $AC -print0 \
|
||||
| while read -d $'\0' F ; do cp "$CDEF/$AC" "$F" >/dev/null ; done
|
||||
|
||||
# Update the %VERSION% in the README.md file
|
||||
VERS=$( echo $EXPORT | sed 's/release-//' )
|
||||
eval "sed -E -i~ -e 's/%VERSION%/$VERS/g' README.md"
|
||||
rm -f README.md~
|
||||
|
||||
# Commit the 'BASE', ready for customizations
|
||||
git add . >/dev/null && git commit --amend --no-edit >/dev/null
|
||||
|
||||
# Create a new branch from 'BASE' for the final result
|
||||
echo "- Creating 'built-temp' branch..."
|
||||
git checkout -b built-temp >/dev/null || exit
|
||||
|
||||
# Delete temporary branch
|
||||
git branch -D BASE 2>/dev/null
|
||||
|
||||
echo "- Applying customizations..."
|
||||
cp -R "$TEMP/config" .
|
||||
find config -type f \! -name "Configuration*" -exec rm "{}" \;
|
||||
|
||||
addpathlabels() {
|
||||
cd $CEXA
|
||||
find . -name "Conf*.h" -print0 | while read -d $'\0' fn ; do
|
||||
fldr=$(dirname "$fn" | sed "s/^\.\///")
|
||||
blank_line=$(awk '/^\s*$/ {print NR; exit}' "$fn")
|
||||
sed -i~ "${blank_line}i\\\n#define CONFIG_EXAMPLES_DIR \"$fldr\"" "$fn"
|
||||
rm -f "$fn~"
|
||||
done
|
||||
cd -
|
||||
}
|
||||
|
||||
echo "- Applying path labels..."
|
||||
addpathlabels >/dev/null 2>&1
|
||||
|
||||
git add . >/dev/null && git commit -m "Examples Customizations" >/dev/null
|
||||
|
||||
echo "- Adding all the extras..."
|
||||
cp -R "$TEMP/config" .
|
||||
|
||||
echo "- Applying path labels..."
|
||||
addpathlabels >/dev/null 2>&1
|
||||
|
||||
git add . >/dev/null && git commit -m "Examples Extras" >/dev/null
|
||||
|
||||
echo "- Replace $EXPORT branch"
|
||||
git fetch origin $EXPORT >/dev/null
|
||||
git checkout >/dev/null $EXPORT
|
||||
git reset --hard built-temp
|
||||
git push -f
|
||||
git branch -D built-temp
|
||||
|
||||
rm -rf $TEMP
|
||||
run: bin/mfconfig CI
|
||||
|
|
250
bin/mfconfig
Executable file
250
bin/mfconfig
Executable file
|
@ -0,0 +1,250 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mfconfig [manual|init|repath] [source] [dest] [repo-path]
|
||||
#
|
||||
# Operate on the MarlinFirmware/Configurations repository.
|
||||
#
|
||||
# The MarlinFirmware/Configurations layout could be broken up into branches,
|
||||
# but this makes management more complicated and requires more commits to
|
||||
# perform the same operation, so this uses a single branch with subfolders.
|
||||
#
|
||||
# init - Initialize the repo with a base commit and changes:
|
||||
# - Source will be an 'import' branch containing all current configs.
|
||||
# - Create an empty 'WORK' branch from 'init-repo'.
|
||||
# - Add Marlin config files, but reset all to defaults.
|
||||
# - Commit this so changes will be clear in following commits.
|
||||
# - Add changed Marlin config files and commit.
|
||||
#
|
||||
# manual - Import changes from a local Marlin folder, then init.
|
||||
# - Replace 'default' configs with your local Marlin configs.
|
||||
# - Wait for manual propagation to the rest of the configs.
|
||||
# - Run init with the given 'source' and 'dest'
|
||||
#
|
||||
# repath - Add path labels to all config files, if needed
|
||||
# - Add a #define CONFIG_EXAMPLES_DIR to each Configuration*.h file.
|
||||
#
|
||||
# CI - Run in CI mode, using the current folder as the repo.
|
||||
# - For GitHub Actions to update the Configurations repo.
|
||||
#
|
||||
import os, sys, subprocess, shutil, datetime, tempfile
|
||||
from pathlib import Path
|
||||
|
||||
# Set to 1 for extra debug commits (no deployment)
|
||||
DEBUG = 0
|
||||
|
||||
# Get the shell arguments into ACTION, IMPORT, and EXPORT
|
||||
ACTION = sys.argv[1] if len(sys.argv) > 1 else 'manual'
|
||||
IMPORT = sys.argv[2] if len(sys.argv) > 2 else 'import-2.1.x'
|
||||
EXPORT = sys.argv[3] if len(sys.argv) > 3 else 'bugfix-2.1.x'
|
||||
|
||||
# Get repo paths
|
||||
CI = False
|
||||
if ACTION == 'CI':
|
||||
_REPOS = "."
|
||||
REPOS = Path(_REPOS)
|
||||
CONFIGREPO = REPOS
|
||||
ACTION = 'init'
|
||||
CI = True
|
||||
else:
|
||||
_REPOS = sys.argv[4] if len(sys.argv) > 4 else '~/Projects/Maker/Firmware'
|
||||
REPOS = Path(_REPOS).expanduser()
|
||||
CONFIGREPO = REPOS / "Configurations"
|
||||
|
||||
def usage():
|
||||
print(f"Usage: {os.path.basename(sys.argv[0])} [manual|init|repath] [source] [dest] [repo-path]")
|
||||
|
||||
if ACTION not in ('manual','init','repath'):
|
||||
print(f"Unknown action '{ACTION}'")
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
CONFIGCON = CONFIGREPO / "config"
|
||||
CONFIGDEF = CONFIGCON / "default"
|
||||
CONFIGEXA = CONFIGCON / "examples"
|
||||
|
||||
# Configurations repo folder must exist
|
||||
if not CONFIGREPO.exists():
|
||||
print(f"Can't find Configurations repo at {_REPOS}")
|
||||
sys.exit(1)
|
||||
|
||||
# Run git within CONFIGREPO
|
||||
GITSTDERR = None if DEBUG else subprocess.DEVNULL
|
||||
def git(etc):
|
||||
if DEBUG:
|
||||
print(f"> git {' '.join(etc)}")
|
||||
if etc[0] == "push":
|
||||
info("*** DRY RUN ***")
|
||||
return subprocess.run(["echo"])
|
||||
return subprocess.run(["git"] + etc, cwd=CONFIGREPO, stdout=subprocess.PIPE, stderr=GITSTDERR)
|
||||
|
||||
# Get the current branch name
|
||||
def branch(): return git(["rev-parse", "--abbrev-ref", "HEAD"])
|
||||
|
||||
# git add . ; git commit -m ...
|
||||
def commit(msg, who="."): git(["add", who]) ; return git(["commit", "-m", msg])
|
||||
|
||||
# git checkout ...
|
||||
def checkout(etc): return git(["checkout"] + ([etc] if isinstance(etc, str) else etc))
|
||||
|
||||
# git branch -D ...
|
||||
def gitbd(name): return git(["branch", "-D", name]).stdout
|
||||
|
||||
# git status --porcelain : to check for changes
|
||||
def changes(): return git(["status", "--porcelain"]).stdout.decode().strip()
|
||||
|
||||
# Configure git user
|
||||
git(["config", "user.email", "thinkyhead@users.noreply.github.com"])
|
||||
git(["config", "user.name", "Scott Lahteine"])
|
||||
|
||||
# Stash uncommitted changes at the destination?
|
||||
if changes():
|
||||
print(f"There are uncommitted Configurations repo changes.")
|
||||
STASH_YES = input("Stash changes? [Y/n] ") ; print()
|
||||
if STASH_YES not in ('Y','y',''): print("Can't continue") ; sys.exit()
|
||||
git(["stash", "-m", f"!!GitHub_Desktop<{branch()}>"])
|
||||
if changes(): print(f"Can't stash changes!") ; sys.exit(1)
|
||||
|
||||
def info(msg):
|
||||
infotag = "[INFO] " if CI else ""
|
||||
print(f"- {infotag}{msg}")
|
||||
|
||||
def add_path_labels():
|
||||
info("Adding path labels to all configs...")
|
||||
for fn in CONFIGEXA.glob("**/Configuration*.h"):
|
||||
fldr = str(fn.parent.relative_to(CONFIGCON)).replace("examples/", "")
|
||||
with open(fn, 'r') as f:
|
||||
lines = f.readlines()
|
||||
emptyline = -1
|
||||
for i, line in enumerate(lines):
|
||||
issp = line.isspace()
|
||||
if emptyline < 0:
|
||||
if issp: emptyline = i
|
||||
elif not issp:
|
||||
if not "CONFIG_EXAMPLES_DIR" in line:
|
||||
lines.insert(emptyline, f"\n#define CONFIG_EXAMPLES_DIR \"{fldr}\"\n")
|
||||
with open(fn, 'w') as f: f.writelines(lines)
|
||||
break
|
||||
|
||||
if ACTION == "repath":
|
||||
add_path_labels()
|
||||
|
||||
elif ACTION == "manual":
|
||||
|
||||
MARLINREPO = Path(REPOS / "MarlinFirmware")
|
||||
if not MARLINREPO.exists():
|
||||
print("Can't find MarlinFirmware at {_REPOS}!")
|
||||
sys.exit(1)
|
||||
|
||||
info(f"Updating '{IMPORT}' from Marlin...")
|
||||
|
||||
checkout(IMPORT)
|
||||
|
||||
# Replace examples/default with our local copies
|
||||
shutil.copy(MARLINREPO / "Marlin" / "Configuration*.h", CONFIGDEF)
|
||||
|
||||
#git add . && git commit -m "Changes from Marlin ($(date '+%Y-%m-%d %H:%M'))."
|
||||
#commit(f"Changes from Marlin ({datetime.datetime.now()}).")
|
||||
|
||||
print(f"Prepare the import branch and continue when ready.")
|
||||
INIT_YES = input("Ready to init? [y/N] ") ; print()
|
||||
if INIT_YES not in ('Y','y'): print("Done.") ; sys.exit()
|
||||
|
||||
ACTION = 'init'
|
||||
|
||||
if ACTION == "init":
|
||||
print(f"Building branch '{EXPORT}'...")
|
||||
info("Init WORK branch...")
|
||||
|
||||
info(f"Copy {IMPORT} to temporary location...")
|
||||
|
||||
# Use the import branch as the source
|
||||
result = checkout(IMPORT)
|
||||
if result.returncode != 0:
|
||||
print(f"Can't find branch '{IMPORT}'!") ; sys.exit()
|
||||
|
||||
# Copy to a temporary location
|
||||
TEMP = Path(tempfile.mkdtemp())
|
||||
TEMPCON = TEMP / "config"
|
||||
shutil.copytree(CONFIGCON, TEMPCON)
|
||||
|
||||
# Strip #error lines from Configuration.h
|
||||
for fn in TEMPCON.glob("**/Configuration.h"):
|
||||
with open(fn, 'r') as f:
|
||||
lines = f.readlines()
|
||||
outlines = []
|
||||
for line in lines:
|
||||
if not line.startswith("#error"):
|
||||
outlines.append(line)
|
||||
with open(fn, 'w') as f:
|
||||
f.writelines(outlines)
|
||||
|
||||
# Create a fresh 'WORK' as a copy of 'init-repo' (README, LICENSE, etc.)
|
||||
gitbd("WORK")
|
||||
if CI: git(["fetch", "origin", "init-repo"])
|
||||
checkout(["init-repo", "-b", "WORK"])
|
||||
|
||||
# Copy default configurations into the repo
|
||||
info("Create configs in default state...")
|
||||
for fn in TEMPCON.glob("**/*"):
|
||||
if fn.is_dir(): continue
|
||||
relpath = fn.relative_to(TEMPCON)
|
||||
os.makedirs(CONFIGCON / os.path.dirname(relpath), exist_ok=True)
|
||||
if fn.name.startswith("Configuration"):
|
||||
shutil.copy(TEMPCON / "default" / fn.name, CONFIGCON / relpath)
|
||||
|
||||
# DEBUG: Commit the reset for review
|
||||
if DEBUG: commit("[DEBUG] Create defaults")
|
||||
|
||||
def replace_in_file(fn, search, replace):
|
||||
with open(fn, 'r') as f: lines = f.read()
|
||||
with open(fn, 'w') as f: f.write(lines.replace(search, replace))
|
||||
|
||||
# Update the %VERSION% in the README.md file
|
||||
replace_in_file(CONFIGREPO / "README.md", "%VERSION%", EXPORT.replace("release-", ""))
|
||||
|
||||
# Commit all changes up to now; amend if not debugging
|
||||
if DEBUG:
|
||||
commit("[DEBUG] Update README.md version", "README.md")
|
||||
else:
|
||||
git(["add", "."])
|
||||
git(["commit", "--amend", "--no-edit"])
|
||||
|
||||
# Copy configured Configuration*.h to the working copy
|
||||
info("Copy examples into place...")
|
||||
for fn in TEMPCON.glob("examples/**/Configuration*.h"):
|
||||
shutil.copy(fn, CONFIGCON / fn.relative_to(TEMPCON))
|
||||
|
||||
# Put #define CONFIG_EXAMPLES_DIR .. before the first blank line
|
||||
add_path_labels()
|
||||
|
||||
info("Commit config changes...")
|
||||
commit("Examples Customizations")
|
||||
|
||||
# Copy over all files not matching Configuration*.h to the working copy
|
||||
info("Copy extras into place...")
|
||||
for fn in TEMPCON.glob("examples/**/*"):
|
||||
if fn.is_dir(): continue
|
||||
if fn.name.startswith("Configuration"): continue
|
||||
shutil.copy(fn, CONFIGCON / fn.relative_to(TEMPCON))
|
||||
|
||||
info("Commit extras...")
|
||||
commit("Examples Extras")
|
||||
|
||||
# Delete the temporary folder
|
||||
shutil.rmtree(TEMP)
|
||||
|
||||
# Push to the remote (if desired)
|
||||
if CI:
|
||||
PUSH_YES = 'Y'
|
||||
else:
|
||||
print()
|
||||
PUSH_YES = input(f"Push to upstream/{EXPORT}? [y/N] ")
|
||||
print()
|
||||
|
||||
REMOTE = "origin" if CI else "upstream"
|
||||
|
||||
if PUSH_YES in ('Y','y'):
|
||||
info("Push to remote...")
|
||||
git(["push", "-f", REMOTE, f"WORK:{EXPORT}"])
|
||||
|
||||
info("Done.")
|
Loading…
Add table
Add a link
Reference in a new issue