Skip to content
Merged
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
7 changes: 5 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fix git ownership for container
run: |
git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Verify tag/version consistency
id: ver
run: |
Expand All @@ -34,7 +37,7 @@ jobs:
- name: Build
run: |
make clean
make keychain-$(cat VERSION).tar.gz
make dist/keychain-$(cat VERSION).tar.gz
- name: Test space-in-home handling
run: |
ver=$(cat VERSION)
Expand All @@ -52,7 +55,7 @@ jobs:
with:
name: keychain-${{ steps.ver.outputs.version }}-artifacts
path: |
keychain-${{ steps.ver.outputs.version }}.tar.gz
dist/keychain-${{ steps.ver.outputs.version }}.tar.gz
keychain
keychain.1
.release-notes.md
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ keychain.txt
keychain.spec
.specstory/
.ci-artifacts*/
/keychain-*.tar.gz
dist/
18 changes: 18 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# ChangeLog for Keychain - https://github.com/danielrobbins/keychain

## keychain 2.9.8 (2 Nov 2025)

This release fixes the release tarball to include all necessary files for building and using keychain.

Bug fixes:

* Fixed release tarball generation to include bash completion script (`completions/keychain.bash`),
Makefile, source files, and other essential components. Previous release (2.9.7) tarball was
missing these files.
* Improved tarball generation to use `git archive` as source of truth, eliminating manual file
inventory and preventing future omissions.
* Updated release logic to use `dist/` directory for archive generation. GitHub workflow plumbing
work for new `/dist` tarball location, associated `Makefile` and CI fixes.

Documentation:

* Added bash completion information to keychain man page (NOTES section).

## keychain 2.9.7 (31 Oct 2025)

This release fixes critical issues with spaces in HOME directories and usernames, and adds official Git Bash on Windows compatibility.
Expand Down
26 changes: 13 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ Y ?= $(shell date +'%Y')
PREFIX ?= /usr/local
COMPLETIONSDIR ?= $(PREFIX)/share/bash-completion/completions

TARBALL_CONTENTS=keychain README.md ChangeLog.md COPYING.txt MAINTAINERS.txt keychain.pod keychain.1 keychain.spec

all: keychain.1 keychain keychain.spec

.PHONY : tmpclean
Expand All @@ -23,10 +21,10 @@ tmpclean:
clean: tmpclean
rm -rf keychain.1 keychain keychain.spec

keychain.spec: keychain.spec.in keychain.sh
keychain.spec: keychain.spec.in keychain.sh VERSION
sed 's/KEYCHAIN_VERSION/$V/' keychain.spec.in > keychain.spec

keychain.1: keychain.pod keychain.sh
keychain.1: keychain.pod keychain.sh VERSION
pod2man --name=keychain --release=$V \
--center='https://github.com/danielrobbins/keychain' \
keychain.pod keychain.1
Expand Down Expand Up @@ -61,28 +59,30 @@ keychain: keychain.sh keychain.txt VERSION MAINTAINERS.txt
keychain.txt: keychain.pod
pod2text keychain.pod keychain.txt

keychain-$V.tar.gz: $(TARBALL_CONTENTS)
mkdir keychain-$V
cp $(TARBALL_CONTENTS) keychain-$V
/bin/tar czvf keychain-$V.tar.gz keychain-$V
rm -rf keychain-$V
ls -l keychain-$V.tar.gz
dist/keychain-$V.tar.gz: keychain keychain.1 keychain.spec
mkdir -p dist
rm -rf dist/keychain-$V
git archive --format=tar --prefix=keychain-$V/ HEAD | tar -xf - -C dist/
cp keychain keychain.1 keychain.spec dist/keychain-$V/
tar -C dist -czf dist/keychain-$V.tar.gz keychain-$V
rm -rf dist/keychain-$V
ls -l dist/keychain-$V.tar.gz

# --- Release Automation Helpers ---
.PHONY: release release-refresh

RELEASE_ASSETS=keychain-$V.tar.gz keychain keychain.1
RELEASE_ASSETS=dist/keychain-$V.tar.gz keychain keychain.1

# "release" will orchestrate a tagged release with CI artifact validation & confirmation.
release: $(RELEASE_ASSETS)
release: clean $(RELEASE_ASSETS)
@echo "Orchestrating release $(V)"; \
if [ -z "$$GITHUB_TOKEN" ]; then \
echo "GITHUB_TOKEN not set; export a repo-scoped token to proceed." >&2; exit 1; \
fi; \
./scripts/release-orchestrate.sh create $(V)

# "release-refresh" updates assets of an existing GitHub release (e.g. fixups) with CI validation.
release-refresh: $(RELEASE_ASSETS)
release-refresh: clean $(RELEASE_ASSETS)
@echo "Orchestrating release-refresh $(V)"; \
if [ -z "$$GITHUB_TOKEN" ]; then \
echo "GITHUB_TOKEN not set; export a repo-scoped token to proceed." >&2; exit 1; \
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.9.7
2.9.8
14 changes: 7 additions & 7 deletions docs/release-steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This document defines the standard release process. Releases use **numeric tags
## 3. Build Artifacts
Manual build (optional; `make release` now auto-rebuilds prerequisites):
```
make clean && make keychain-$(cat VERSION).tar.gz
make clean && make dist/keychain-$(cat VERSION).tar.gz
```
`make release` or `make release-refresh` will ensure these artifacts exist automatically.

Expand All @@ -26,7 +26,7 @@ Artifacts:
- `keychain.1` (man page)
- `keychain.spec`
- `keychain.txt`
- `keychain-<version>.tar.gz`
- `dist/keychain-<version>.tar.gz`

## 4. Local Sanity Tests
```
Expand Down Expand Up @@ -62,7 +62,7 @@ You will see:
3. Normalized comparison phase (LOCAL vs CI build):
* `keychain` – raw sha256 digest compare.
* `keychain.1` – raw hash first; if different, re-compare with the Pod::Man auto-generated first line stripped. A normalized match counts as a match (header differences ignored).
* `keychain-<version>.tar.gz` – unpack both tarballs; compare sorted file list and per-file sha256 (man page internally also normalized on first line). Blob-level tar/gzip metadata differences (mtime, uid, compression variance) are ignored if internal contents match.
* `dist/keychain-<version>.tar.gz` – unpack both tarballs; compare sorted file list and per-file sha256 (man page internally also normalized on first line). Blob-level tar/gzip metadata differences (mtime, uid, compression variance) are ignored if internal contents match.
Outcome:
- If all artifacts match (raw or normalized) -> Release uses the CI artifact files directly (local artifacts remain untouched for auditing).
- If any real content mismatch exists -> Abort.
Expand Down Expand Up @@ -92,7 +92,7 @@ Both require `GITHUB_TOKEN` (repo scope) exported in the environment.
If you forgot something (docs only, same version):
```
# Edit ChangeLog.md (if you need to adjust text; refresh will regenerate release notes from current ChangeLog plus provenance.)
# Rebuild if needed (optional): make keychain-$(cat VERSION).tar.gz
# Rebuild if needed (optional): make dist/keychain-$(cat VERSION).tar.gz
make release-refresh
```
You will again get CI fetch attempt, comparisons, preview, and prompt.
Expand Down Expand Up @@ -121,16 +121,16 @@ awk -v ver="$version" '/^## keychain 'ver' /{f=1;print;next} /^## keychain /&&f

## 13. Verification Matrix
| Item | Location | Must Match |
|------|----------|-----------|
|------|----------|------------|
| Version tag | git tag | `VERSION` file |
| Wrapper script | `keychain` | contains version string |
| Man page header | `keychain.1` | version/date/center URL |
| Tarball name | `keychain-<version>.tar.gz` | version |
| Tarball name | `dist/keychain-<version>.tar.gz` | version |

## 14. Minimal Quick Release Recap
```
$EDITOR ChangeLog.md VERSION
make clean && make keychain-$(cat VERSION).tar.gz
make clean && make dist/keychain-$(cat VERSION).tar.gz
./keychain --version
git tag -s $(cat VERSION) -m "$(cat VERSION)"
git push && git push --tags
Expand Down
6 changes: 6 additions & 0 deletions keychain.pod
Original file line number Diff line number Diff line change
Expand Up @@ -737,5 +737,11 @@ Keychain was created and is currently maintained by Daniel Robbins. To report a
bug or request an enhancement, use the issue tracker at
L<https://github.com/danielrobbins/keychain>.

Keychain includes bash completion support for command-line options, SSH keys,
GPG keys, and extended mode prefixes (C<sshk:>, C<gpgk:>, C<host:>). The
completion script is included in the source tarball at
C<completions/keychain.bash>. For installation instructions, see the README
file or run C<make install-completions>.

The former Funtoo Linux wiki page is preserved only as an historical reference:
L<https://www.funtoo.org/Funtoo:Keychain>.
2 changes: 1 addition & 1 deletion scripts/release-create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ notes_file=$(mktemp)
# Artifact path vars (provided by orchestrator if using CI artifacts)
ASSET_KEYCHAIN=${KEYCHAIN_ASSET_KEYCHAIN:-keychain}
ASSET_MAN=${KEYCHAIN_ASSET_MAN:-keychain.1}
ASSET_TARBALL=${KEYCHAIN_ASSET_TARBALL:-keychain-$VER.tar.gz}
ASSET_TARBALL=${KEYCHAIN_ASSET_TARBALL:-dist/keychain-$VER.tar.gz}

echo "Creating release $VER"
json=$(mktemp)
Expand Down
60 changes: 37 additions & 23 deletions scripts/release-orchestrate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ if [ "$MODE" = create ]; then
fi

# 1. Ensure local assets exist
for f in keychain-$VER.tar.gz keychain keychain.1; do
for f in dist/keychain-$VER.tar.gz keychain keychain.1; do
[ -f "$f" ] || { echo "Missing local asset: $f" >&2; exit 1; }
done
done

# 2. Fetch CI artifacts (MANDATORY)
CI_DIR=".ci-artifacts-$VER"
Expand Down Expand Up @@ -89,13 +89,19 @@ compare_tar_content() {
h2=$(tail -n +2 "$tmp_ci/$root/$rel" | sha256sum | awk '{print $1}')
if [ "$h1" != "$h2" ]; then
echo " keychain-$VER.tar.gz: content mismatch in $rel (beyond header)" >&2
echo "--- LOCAL: $rel" >&2
echo "+++ CI: $rel" >&2
diff -u <(tail -n +2 "$tmp_local/$root/$rel") <(tail -n +2 "$tmp_ci/$root/$rel") | head -20 >&2
mismatch=1
fi
else
h1=$(sha256sum "$tmp_local/$root/$rel" | awk '{print $1}')
h2=$(sha256sum "$tmp_ci/$root/$rel" | awk '{print $1}')
if [ "$h1" != "$h2" ]; then
echo " keychain-$VER.tar.gz: content mismatch in $rel" >&2
echo "--- LOCAL: $rel" >&2
echo "+++ CI: $rel" >&2
diff -u "$tmp_local/$root/$rel" "$tmp_ci/$root/$rel" | head -20 >&2
mismatch=1
fi
fi
Expand All @@ -111,48 +117,56 @@ EOF
}

# Process artifacts with specialized logic
for artifact in keychain keychain.1 keychain-$VER.tar.gz; do
if [ ! -f "$CI_DIR/$artifact" ]; then
printf ' %-20s CI copy missing; comparison failed (abort)\n' "$artifact"
for artifact in keychain keychain.1 dist/keychain-$VER.tar.gz; do
# Get basename for CI comparison
basename_artifact=$(basename "$artifact")
# Determine CI path (tarball is in dist/ subdirectory)
if [ "$basename_artifact" = "keychain-$VER.tar.gz" ]; then
ci_artifact_path="$CI_DIR/dist/$basename_artifact"
else
ci_artifact_path="$CI_DIR/$basename_artifact"
fi
if [ ! -f "$ci_artifact_path" ]; then
printf ' %-20s CI copy missing; comparison failed (abort)\n' "$basename_artifact"
diff_flag=1
continue
fi
case "$artifact" in
case "$basename_artifact" in
keychain)
L=$(calc_sha256 "$artifact"); R=$(calc_sha256 "$CI_DIR/$artifact")
L=$(calc_sha256 "$artifact"); R=$(calc_sha256 "$ci_artifact_path")
if [ "$L" = "$R" ]; then
printf ' %-20s %s (match)\n' "$artifact" "$L"
printf ' %-20s %s (match)\n' "$basename_artifact" "$L"
else
printf ' %-20s LOCAL %s != CI %s *DIFF*\n' "$artifact" "$L" "$R"
printf ' %-20s LOCAL %s != CI %s *DIFF*\n' "$basename_artifact" "$L" "$R"
diff_flag=1
fi
;;
keychain.1)
# Direct hash first
L=$(calc_sha256 "$artifact"); R=$(calc_sha256 "$CI_DIR/$artifact")
L=$(calc_sha256 "$artifact"); R=$(calc_sha256 "$ci_artifact_path")
if [ "$L" = "$R" ]; then
printf ' %-20s %s (match)\n' "$artifact" "$L"
printf ' %-20s %s (match)\n' "$basename_artifact" "$L"
else
# Normalize and compare ignoring Pod::Man header line.
if diff -u <(tail -n +2 "$artifact") <(tail -n +2 "$CI_DIR/$artifact") >/dev/null 2>&1; then
printf ' %-20s (normalized match ignoring Pod::Man header)\n' "$artifact"
if diff -u <(tail -n +2 "$artifact") <(tail -n +2 "$ci_artifact_path") >/dev/null 2>&1; then
printf ' %-20s (normalized match ignoring Pod::Man header)\n' "$basename_artifact"
else
printf ' %-20s LOCAL %s != CI %s *DIFF* (content mismatch beyond header)\n' "$artifact" "$L" "$R"
printf ' %-20s LOCAL %s != CI %s *DIFF* (content mismatch beyond header)\n' "$basename_artifact" "$L" "$R"
diff_flag=1
fi
fi
;;
"keychain-$VER.tar.gz")
if compare_tar_content "$artifact" "$CI_DIR/$artifact"; then
if compare_tar_content "$artifact" "$ci_artifact_path"; then
# If tar blob hash matches display it; else note normalized match.
L=$(calc_sha256 "$artifact"); R=$(calc_sha256 "$CI_DIR/$artifact")
L=$(calc_sha256 "$artifact"); R=$(calc_sha256 "$ci_artifact_path")
if [ "$L" = "$R" ]; then
printf ' %-20s %s (match)\n' "$artifact" "$L"
printf ' %-20s %s (match)\n' "$basename_artifact" "$L"
else
printf ' %-20s (content match; tar/gzip metadata differ)\n' "$artifact"
printf ' %-20s (content match; tar/gzip metadata differ)\n' "$basename_artifact"
fi
else
printf ' %-20s *CONTENT DIFF* (see above messages)\n' "$artifact"
printf ' %-20s *CONTENT DIFF* (see above messages)\n' "$basename_artifact"
diff_flag=1
fi
;;
Expand All @@ -168,8 +182,8 @@ if [ $diff_flag -ne 0 ]; then
echo " VER=$VER; CI_DIR=$CI_DIR" >&2
echo " diff -u keychain \"$CI_DIR/keychain\"" >&2
echo " diff -u keychain.1 \"$CI_DIR/keychain.1\"" >&2
echo " diff -u <(tar tzf keychain-$VER.tar.gz | sort) <(tar tzf $CI_DIR/keychain-$VER.tar.gz | sort)" >&2
echo " mkdir -p /tmp/kc-local /tmp/kc-ci && tar xzf keychain-$VER.tar.gz -C /tmp/kc-local && tar xzf $CI_DIR/keychain-$VER.tar.gz -C /tmp/kc-ci && diff -ru /tmp/kc-local/keychain-$VER /tmp/kc-ci/keychain-$VER" >&2
echo " diff -u <(tar tzf dist/keychain-$VER.tar.gz | sort) <(tar tzf $CI_DIR/dist/keychain-$VER.tar.gz | sort)" >&2
echo " mkdir -p /tmp/kc-local /tmp/kc-ci && tar xzf dist/keychain-$VER.tar.gz -C /tmp/kc-local && tar xzf $CI_DIR/dist/keychain-$VER.tar.gz -C /tmp/kc-ci && diff -ru /tmp/kc-local/keychain-$VER /tmp/kc-ci/keychain-$VER" >&2
echo
if [ "${KEYCHAIN_FORCE_LOCAL:-}" = 1 ]; then
echo "KEYCHAIN_FORCE_LOCAL=1 set: proceeding using LOCAL artifacts despite mismatches." >&2
Expand All @@ -182,13 +196,13 @@ fi
if [ "${KEYCHAIN_FORCE_LOCAL:-}" = 1 ]; then
KEYCHAIN_ASSET_KEYCHAIN="keychain"
KEYCHAIN_ASSET_MAN="keychain.1"
KEYCHAIN_ASSET_TARBALL="keychain-$VER.tar.gz"
KEYCHAIN_ASSET_TARBALL="dist/keychain-$VER.tar.gz"
echo "Source selection: USING LOCAL artifacts (override)." >&2
else
# All artifacts matched (raw or normalized) -> use CI versions
KEYCHAIN_ASSET_KEYCHAIN="$CI_DIR/keychain"
KEYCHAIN_ASSET_MAN="$CI_DIR/keychain.1"
KEYCHAIN_ASSET_TARBALL="$CI_DIR/keychain-$VER.tar.gz"
KEYCHAIN_ASSET_TARBALL="$CI_DIR/dist/keychain-$VER.tar.gz"
echo "Source selection: USING CI artifacts (canonical)." >&2
fi
export KEYCHAIN_ASSET_KEYCHAIN KEYCHAIN_ASSET_MAN KEYCHAIN_ASSET_TARBALL
Expand Down
2 changes: 1 addition & 1 deletion scripts/release-refresh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ done
echo "Uploading replacement assets..."
ASSET_KEYCHAIN=${KEYCHAIN_ASSET_KEYCHAIN:-keychain}
ASSET_MAN=${KEYCHAIN_ASSET_MAN:-keychain.1}
ASSET_TARBALL=${KEYCHAIN_ASSET_TARBALL:-keychain-$VER.tar.gz}
ASSET_TARBALL=${KEYCHAIN_ASSET_TARBALL:-dist/keychain-$VER.tar.gz}

# (Note: By default we do not modify existing release notes. Set KEYCHAIN_UPDATE_NOTES=1 to rebuild.)

Expand Down