A gh
extension to publish content from source repository into multiple target repositories.
Built on top of gruntwork-io/git-xargs
, gh-publicize
is more
opinionated on how to publish content from a centralized location with specific default behaviors:
- Use of a centralized source repository to publish content from, whether used as a template repository or not
- Leverage scripts from source repository, smoothing over need for fully qualified scripts
- Provide helper library for shell scripts to simplify managing content
- Avoid making changes unless explicitly indicated (
-r,--run
flag) - Avoid archived repositories unless explicitly indicated (
-a,--include-archived-repos
flag)
- Download and install git-xargs
gh extension install andyfeller/gh-publicize
gh publicize --repo=<owner>/<repo> <cmd>
gh publicize --repo=<owner>/<repo> --run <cmd>
- Profit! π° πΈ π€ πΈ π°
Note
gh-publicize
requires the use of coarse-grained v1 PAT token withrepo
scope.
Publish content in target repositories from source repository.
USAGE
gh publicize [flags] --repo=<owner>/<repo> [...] <cmd>
gh publicize [flags] --repos=<file> <cmd>
gh publicize [flags] --org=<org> <cmd>
FLAGS
-a, --include-archived-repos Whether to include archived repositories
-c, --cache-dir=<cache-dir> Name of directory containing preserved data to reuse
-C, --commit-message=<msg> Commit message to use when pushing changes
-d, --debug Enable debug logging
-h, --help Displays help usage
-m, --repo=<owner>/<repo> Target a specific repo, can be passed multiple times to target several repos
-n, --repos=<file> Target multiple repos, file contains <owner>/<repo> entry per line
-o, --org=<org> Target all repos in GitHub organization
-P, --source-repo-path=<dir> Directory within source repository to add to path; default 'bin'
-p, --preserve Preserve temporary directory containing data
-R, --reviewers=<csv-usernames> Comma-separated list of usernames to review pull request
-r, --run Apply changes; defaults to dryrun
-s, --source-repo=<owner>/<repo> Name of source repository to clone
-t, --source-repo-revision=<rev> Revision of source repository to checkout; default 'main'
ENVIRONMENT VARIABLES
PUBLICIZE_HOME Path to gh-publicize
PUBLICIZE_LIB Path to gh-publicize/lib
PUBLICIZE_SOURCE_DIR Path to source repository cloned for use
When creating a new GitHub repository, there are several common needs that may go overlooked:
- Adding a code owners file
- Adding a code of conduct such as Contributor Covenant
- Adding a license from choosealicense.com
- Adding a
.gitignore
based upon gitignore.io
One option would be using a template repository, except it relies upon people to use it
and only supports static content. This is where gh-publicize
can offer a reactive approach using a source repository
and a simple shell script.
-
Source Repository
. βββ CODEOWNERS βββ CODE_OF_CONDUCT.md βββ LICENSE.txt βββ README.md βββ bin βββ 00-base.sh
-
Shell Script
bin/00-base.sh
#! /usr/bin/env bash # Invoke command(s) and/or script(s) doing work, assuming environment including PWD will be preserved for invoked scripts source $PUBLICIZE_LIB/helpers.sh # Ensure base files every repository needs are there if missing; do not override copyMissingFile $PUBLICIZE_SOURCE_DIR ".gitignore" copyMissingFile $PUBLICIZE_SOURCE_DIR "CODEOWNERS" copyMissingFile $PUBLICIZE_SOURCE_DIR "CODE_OF_CONDUCT.md" copyMissingFile $PUBLICIZE_SOURCE_DIR "LICENSE.txt" # Update repository labels as appropriate updateLabels
The source repository above - which may be a template repository or not - contains static content and
scripts I want within all of my repositories. The bin/00-base.sh
script leverages helper functions
to copy missing files and update labels when invoked by gh-publicize
.
In the following commands, gh-publicize
will execute 00-base.sh
from andyfeller/template
in
dryrun mode then run mode, creating pull requests for multiple repositories:
-
Create repositories for testing:
$ gh repo create andyfeller/test-1 --private --add-readme $ gh repo create andyfeller/test-2 --private --add-readme
-
Run
gh-publicize
in dryrun mode:$ gh publicize --repo=andyfeller/test-1 --repo=andyfeller/test-2 --source-repo=andyfeller/template 00-base.sh
gh publicize
outputCreated temporary directory for caching data: /var/folders/xb/svzskj1x77x3qsmwx1d84nqc0000gn/T/gh-publicizeXXX.BMdK3T1L Cloning andyfeller/template, checking out main Cloning into '/var/folders/xb/svzskj1x77x3qsmwx1d84nqc0000gn/T/gh-publicizeXXX.BMdK3T1L/_source-repo'... remote: Enumerating objects: 22, done. remote: Counting objects: 100% (22/22), done. remote: Compressing objects: 100% (15/15), done. remote: Total 22 (delta 5), reused 16 (delta 2), pack-reused 0 Receiving objects: 100% (22/22), 6.04 KiB | 3.02 MiB/s, done. Resolving deltas: 100% (5/5), done. Already on 'main' Your branch is up to date with 'origin/main'. Executing git-xargs command [git-xargs] INFO[2023-07-30T17:44:59-04:00] git-xargs running... [git-xargs] INFO[2023-07-30T17:44:59-04:00] Dry run setting enabled. No local branches will be pushed and no PRs will be opened in Github Processing repos [2/2] βββββββββββββββββββββββββββββββββββββββββββββββ 100% | 2s Git-xargs run summary @ 2023-07-30 21:45:04.025872 +0000 UTC β’ Runtime in seconds: 5 β’ Command supplied: [00-base.sh] β’ Repo selection method: repo-flag All repos that were targeted for processing after filtering missing / malformed repos ββββββββββββββββββββββββββββββββββββββββββββββββββββ | Repo name | Repo URL | | test-1 | https://github.com/andyfeller/test-1 | | ------------------------------------------------ | | test-2 | https://github.com/andyfeller/test-2 | ββββββββββββββββββββββββββββββββββββββββββββββββββββ Repos that were successfully cloned to the local filesystem ββββββββββββββββββββββββββββββββββββββββββββββββββββ | Repo name | Repo URL | | test-1 | https://github.com/andyfeller/test-1 | | ------------------------------------------------ | | test-2 | https://github.com/andyfeller/test-2 | ββββββββββββββββββββββββββββββββββββββββββββββββββββ Repos that showed file changes to their working directory following command execution ββββββββββββββββββββββββββββββββββββββββββββββββββββ | Repo name | Repo URL | | test-1 | https://github.com/andyfeller/test-1 | | ------------------------------------------------ | | test-2 | https://github.com/andyfeller/test-2 | ββββββββββββββββββββββββββββββββββββββββββββββββββββ Repos whose local branch was not pushed because the --dry-run flag was set ββββββββββββββββββββββββββββββββββββββββββββββββββββ | Repo name | Repo URL | | test-1 | https://github.com/andyfeller/test-1 | | ------------------------------------------------ | | test-2 | https://github.com/andyfeller/test-2 | ββββββββββββββββββββββββββββββββββββββββββββββββββββ Repos whose specified branches did not exist on the remote, and so were first created locally ββββββββββββββββββββββββββββββββββββββββββββββββββββ | Repo name | Repo URL | | test-1 | https://github.com/andyfeller/test-1 | | ------------------------------------------------ | | test-2 | https://github.com/andyfeller/test-2 | ββββββββββββββββββββββββββββββββββββββββββββββββββββ
-
Run
gh-publicize
in run mode:$ gh publicize --run --repo=andyfeller/test-1 --repo=andyfeller/test-2 --source-repo=andyfeller/template 00-base.sh
gh publicize --run
outputCreated temporary directory for caching data: /var/folders/xb/svzskj1x77x3qsmwx1d84nqc0000gn/T/gh-publicizeXXX.PxYKGc7A Cloning andyfeller/template, checking out main Cloning into '/var/folders/xb/svzskj1x77x3qsmwx1d84nqc0000gn/T/gh-publicizeXXX.PxYKGc7A/_source-repo'... remote: Enumerating objects: 22, done. remote: Counting objects: 100% (22/22), done. remote: Compressing objects: 100% (15/15), done. remote: Total 22 (delta 5), reused 16 (delta 2), pack-reused 0 Receiving objects: 100% (22/22), 6.04 KiB | 3.02 MiB/s, done. Resolving deltas: 100% (5/5), done. Already on 'main' Your branch is up to date with 'origin/main'. Executing git-xargs command [git-xargs] INFO[2023-07-30T17:45:53-04:00] git-xargs running... Processing repos [2/2] βββββββββββββββββββββββββββββββββββββββββββββββ 100% | 4s Git-xargs run summary @ 2023-07-30 21:45:57.525786 +0000 UTC β’ Runtime in seconds: 4 β’ Command supplied: [00-base.sh] β’ Repo selection method: repo-flag All repos that were targeted for processing after filtering missing / malformed repos ββββββββββββββββββββββββββββββββββββββββββββββββββββ | Repo name | Repo URL | | test-1 | https://github.com/andyfeller/test-1 | | ------------------------------------------------ | | test-2 | https://github.com/andyfeller/test-2 | ββββββββββββββββββββββββββββββββββββββββββββββββββββ Repos that were successfully cloned to the local filesystem ββββββββββββββββββββββββββββββββββββββββββββββββββββ | Repo name | Repo URL | | test-1 | https://github.com/andyfeller/test-1 | | ------------------------------------------------ | | test-2 | https://github.com/andyfeller/test-2 | ββββββββββββββββββββββββββββββββββββββββββββββββββββ Repos that showed file changes to their working directory following command execution ββββββββββββββββββββββββββββββββββββββββββββββββββββ | Repo name | Repo URL | | test-1 | https://github.com/andyfeller/test-1 | | ------------------------------------------------ | | test-2 | https://github.com/andyfeller/test-2 | ββββββββββββββββββββββββββββββββββββββββββββββββββββ Repos whose specified branches did not exist on the remote, and so were first created locally ββββββββββββββββββββββββββββββββββββββββββββββββββββ | Repo name | Repo URL | | test-1 | https://github.com/andyfeller/test-1 | | ------------------------------------------------ | | test-2 | https://github.com/andyfeller/test-2 | ββββββββββββββββββββββββββββββββββββββββββββββββββββ Pull requests opened βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | Repo name | Pull request URL | | test-1 | https://github.com/andyfeller/test-1/pull/1 | | ------------------------------------------------------- | | test-2 | https://github.com/andyfeller/test-2/pull/1 | βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Like any other gh
CLI extension, gh-publicize
is trivial to install or upgrade and works on most operating systems:
-
Installation
gh extension install andyfeller/gh-publicize
For more information:
gh extension install
-
Upgrade
gh extension upgrade gh-publicize
For more information:
gh extension upgrade
This effort couldn't have happened without the support from many people, so thank you to the following who helped throughout: