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
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -595,8 +595,14 @@ resharper_xmldoc_space_before_self_closing = true
resharper_xmldoc_wrap_tags_and_pi = false
resharper_xmldoc_wrap_text = true


[{*.har,*.inputactions,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}]
indent_size = 2

# YAML files (match common ecosystem conventions)
[*.{yml,yaml}]
indent_size = 2
tab_width = 2

[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}]
tab_width = 4
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: CI

on:
pull_request:
push:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"

- name: Restore
run: dotnet restore

- name: Test
run: dotnet test ./tests/Keystone.Cli.UnitTests/Keystone.Cli.UnitTests.csproj -c Release
153 changes: 153 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
name: Release

on:
push:
tags:
- "v*.*.*"

jobs:
validate:
name: Validate
runs-on: ubuntu-latest

outputs:
version: ${{ steps.v.outputs.version }}

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"

- name: Extract version from tag
id: v
shell: bash
run: |
echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

- name: Validate csproj version matches tag
shell: bash
run: |
CS_VERSION="$(sed -n 's:.*<Version>\(.*\)</Version>.*:\1:p' ./src/Keystone.Cli/Keystone.Cli.csproj | head -n 1)"
if [[ -z "$CS_VERSION" ]]; then
echo "ERROR: Could not read <Version> from ./src/Keystone.Cli/Keystone.Cli.csproj" >&2
exit 1
fi

if [[ "$CS_VERSION" != "${{ steps.v.outputs.version }}" ]]; then
echo "ERROR: csproj <Version> ($CS_VERSION) does not match tag (v${{ steps.v.outputs.version }})" >&2
exit 1
fi

- name: Run unit tests (gate release)
run: dotnet test ./tests/Keystone.Cli.UnitTests/Keystone.Cli.UnitTests.csproj -c Release

build-assets:
name: Build assets (${{ matrix.rid }})
needs: validate
runs-on: ${{ matrix.runner }}

strategy:
fail-fast: false
matrix:
include:
- runner: macos-latest
rid: osx-arm64
- runner: macos-latest
rid: osx-x64

steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"

- name: Publish (${{ matrix.rid }})
run: dotnet publish ./src/Keystone.Cli/Keystone.Cli.csproj -c Release -r ${{ matrix.rid }}

- name: Package tarball (${{ matrix.rid }})
shell: bash
run: bash ./scripts/package-release.sh "${{ needs.validate.outputs.version }}" "${{ matrix.rid }}"

- name: Upload tarball artifact
uses: actions/upload-artifact@v4
with:
name: dist-${{ matrix.rid }}
path: artifacts/release/keystone-cli_${{ needs.validate.outputs.version }}_${{ matrix.rid }}.tar.gz
if-no-files-found: error

publish-release:
name: Publish Release
needs: [validate, build-assets]
runs-on: ubuntu-latest

permissions:
contents: write

steps:
- name: Download all tarballs
uses: actions/download-artifact@v4
with:
path: dist

- name: Generate checksums.txt
shell: bash
run: |
set -euo pipefail

echo "Release assets:"
files=$(find dist -maxdepth 3 -type f -name '*.tar.gz' -print)
if [[ -z "$files" ]]; then
echo "ERROR: No .tar.gz files found under dist/" >&2
exit 1
fi

printf "%s\n" $files | sort

sha256sum $files | sort > checksums.txt

echo ""
echo "checksums.txt:"
cat checksums.txt

- name: Prepare release notes
shell: bash
run: |
set -euo pipefail

cat > release-body.md <<'EOF'
Automated release for ${GITHUB_REF_NAME}.

SHA256 checksums:
```
EOF

cat checksums.txt >> release-body.md

cat >> release-body.md <<'EOF'
```
EOF

echo ""
echo "release-body.md:"
cat release-body.md

- name: Create GitHub Release and upload assets
uses: softprops/action-gh-release@v2
with:
name: "keystone-cli v${{ needs.validate.outputs.version }}"
body_path: release-body.md
files: |
dist/**/keystone-cli_${{ needs.validate.outputs.version }}_*.tar.gz
checksums.txt
release-body.md
54 changes: 54 additions & 0 deletions .github/workflows/tag-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Tag release

on:
workflow_dispatch:

permissions:
contents: write

jobs:
tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # required to create/push tags reliably

- uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"

- name: Read version from csproj
id: v
shell: bash
run: |
VERSION="$(sed -n 's:.*<Version>\(.*\)</Version>.*:\1:p' ./src/Keystone.Cli/Keystone.Cli.csproj | head -n 1)"
if [[ -z "$VERSION" ]]; then
echo "ERROR: Could not read <Version> from ./src/Keystone.Cli/Keystone.Cli.csproj" >&2
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Run unit tests (gate tagging)
run: dotnet test ./tests/Keystone.Cli.UnitTests/Keystone.Cli.UnitTests.csproj -c Release

- name: Ensure tag does not already exist
shell: bash
run: |
TAG="v${{ steps.v.outputs.version }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "ERROR: Tag already exists: $TAG" >&2
exit 1
fi

- name: Create and push tag
shell: bash
run: |
TAG="v${{ steps.v.outputs.version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "keystone-cli $TAG"
git push origin "$TAG"

- name: Summary
run: echo "Pushed tag v${{ steps.v.outputs.version }}. This will trigger the Release workflow."
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

A command-line interface for Keystone.

- 📦 [Releases](https://github.com/Knight-Owl-Dev/keystone-cli/releases) — binaries & checksums
- 📄 [License & Notices](NOTICE.md)

This CLI is designed to operate alongside official Keystone templates, which define the structure and build behavior for
publishing books and documents. It is part of the broader Keystone ecosystem, which includes:

Expand All @@ -12,7 +15,28 @@ publishing books and documents. It is part of the broader Keystone ecosystem, wh
- [keystone-hello-world](https://github.com/knight-owl-dev/keystone-hello-world) – a "Hello World" sample project
based on the `core-slim` template, demonstrating Keystone capabilities with sample content

For license details and third-party references, see [NOTICE.md](NOTICE.md).
## Installation (macOS)

Keystone CLI is distributed via Homebrew.

First, add the Knight Owl Homebrew tap:

```bash
brew tap Knight-Owl-Dev/tap
```

Then install the CLI:

```bash
brew install keystone-cli
```

After installation, verify that everything is working:

```bash
keystone-cli info
man keystone-cli
```

## Project Structure

Expand Down
Loading