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
9 changes: 9 additions & 0 deletions .github/readme-source.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"version": 1,
"sha256": "fb9ec82ff0653ff474751b88f132ecad6a49dd593e38ae24d237d8b0ed3e5522",
"history": {
"forbidden_commits": [
"6890f718519ce148ecceb9209328236aed16efc1"
]
}
}
87 changes: 87 additions & 0 deletions .github/scripts/check-documentation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
"""Check tracked documentation layout and the reviewed usage README digest."""
import hashlib
import json
import os
from pathlib import Path, PurePosixPath
import subprocess
import sys

MARKDOWN = {'.md', '.markdown', '.mdown', '.mkd', '.mkdn', '.mdwn', '.mdx', '.rmd', '.qmd'}
OTHER_DOCS = {'.rst', '.adoc', '.asciidoc'}


def check(root, include_history=True, history_required=False):
raw = subprocess.check_output(['git', 'ls-files', '--stage', '-z'], cwd=root)
entries = {}
errors = []
for record in raw.split(b'\0'):
if not record:
continue
metadata, encoded = record.split(b'\t', 1)
mode, _oid, stage = metadata.decode().split()
name = encoded.decode('utf-8', 'surrogateescape')
if stage != '0':
errors.append('unmerged index entry: ' + name)
entries[name] = mode
path = PurePosixPath(name)
if name != 'README.md' and (path.suffix.lower() in MARKDOWN | OTHER_DOCS
or path.name.lower().startswith('readme.')):
errors.append('documentation must live in the private docs repository: ' + name)
if entries.get('README.md') != '100644' and entries.get('README.md') != '100755':
errors.append('a regular, tracked root README.md is required')
else:
readme = root / 'README.md'
if readme.is_symlink() or not readme.is_file():
errors.append('root README.md must be a regular file')
else:
try:
body = readme.read_bytes()
if not any(line.startswith('# ') for line in body.decode('utf-8').splitlines()):
errors.append('root README.md needs a product heading')
policy_path = root / '.github/readme-source.json'
if policy_path.is_symlink():
raise ValueError('README source policy cannot be a symlink')
policy = json.loads(policy_path.read_text())
if policy.get('version') != 1:
raise ValueError('unsupported README source policy')
if hashlib.sha256(body).hexdigest() != policy.get('sha256'):
errors.append('README differs from its reviewed publication; update the private source first')
history = policy.get('history')
if history_required and history is None:
raise ValueError('public repositories require a history boundary')
if include_history and history is not None:
if set(history) != {'forbidden_commits'} or not isinstance(history['forbidden_commits'], list) or not history['forbidden_commits']:
raise ValueError('invalid public history boundary')
forbidden = set(history['forbidden_commits'])
if any(not isinstance(oid, str) or len(oid) != 40 or any(c not in '0123456789abcdef' for c in oid) for oid in forbidden):
raise ValueError('invalid original history identity')
shallow = subprocess.check_output(['git', 'rev-parse', '--is-shallow-repository'], cwd=root, text=True).strip()
if shallow != 'false':
raise ValueError('public history verification requires fetch-depth: 0')
ancestors = set(subprocess.check_output(['git', 'rev-list', 'HEAD'], cwd=root, text=True).splitlines())
if ancestors & forbidden:
errors.append('original history was reintroduced; transplant changes onto the cleaned main history')
patterns = [':(icase)*'+suffix for suffix in sorted(MARKDOWN)]
past = subprocess.check_output(['git', 'log', '--format=', '--name-only', '-z', 'HEAD', '--', *patterns], cwd=root)
extra = {name.decode('utf-8', 'surrogateescape') for name in past.split(b'\0') if name and name != b'README.md'}
errors.extend('historical Markdown must remain in private docs: '+name for name in sorted(extra))

except (OSError, ValueError, UnicodeError) as error:
errors.append('invalid README source policy or README: ' + str(error))
return errors


def main():
root = Path(subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip())
errors = check(root, history_required=os.environ.get('REQUIRE_CLEAN_HISTORY') == 'true')
for error in errors:
print(error, file=sys.stderr)
if errors:
return 1
print('Documentation policy passed: one root README.md, reviewed source digest matches.')
return 0


if __name__ == '__main__':
sys.exit(main())
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
- uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0

- name: Validate developer profile and workflow controls
uses: nicodes/tools/workflow-standards@59a948943c2f7bdd782cad48ddfdabb106c80fbd # v0.27.5
uses: nicodes/tools/workflow-standards@66992c7abdb653dac5f7da4a4ec6b7d83737c26c # v0.27.5

- name: Build
uses: ./.github/actions/build
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/documentation-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Documentation policy
on:
push:
pull_request:
merge_group:
workflow_dispatch:
permissions:
contents: read
jobs:
documentation-policy:
name: Documentation policy
runs-on: ubuntu-24.04
timeout-minutes: 3
steps:
- name: Check out source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
fetch-depth: ${{ github.event.repository.private && 1 || 0 }}
- name: Check documentation boundary
env:
REQUIRE_CLEAN_HISTORY: ${{ !github.event.repository.private }}
run: python3 .github/scripts/check-documentation.py
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
# spritesmith-web
<!-- Generated from private documentation source. Do not edit directly. Source SHA256: 84d0f1eac33f6e1f49462caee75b0d7537cc8b384b46dc23f8e6b235493d1f0e -->

For current client installation and usage, see https://github.com/aviorstudio/spritesmith-web#readme.
# spritesmith website

Static Astro landing page for [spritesmith](https://spritesmith.ai). The application is at [app.spritesmith.ai](https://app.spritesmith.ai); the public [Go CLI](https://github.com/aviorstudio/spritesmith) connects to its generation API.

The page introduces agent-created vector sprites and animations, reference images, chat refinement, the library, gallery, and CLI. Artwork and conversations on the page are illustrations, not captured product results.

## Install the CLI

With curl:

```sh
curl -fsSL https://spritesmith.ai/install | sh
```

Or with mise:

```sh
mise use --global github:aviorstudio/spritesmith@latest
```

An icon-only copy control sits on the right inside each install-command outline and shows a checkmark after copying. If clipboard access is unavailable, the command remains selectable for manual copying.

Prebuilt Linux/macOS binaries are available for x86_64 and arm64. A Go compiler is not required. See [spritesmith](https://github.com/aviorstudio/spritesmith#readme) for setup and installation options.
Loading