Skip to content

Commit effd431

Browse files
committed
chore: initial commit for docs branch
Orphan branch holding only the documentation site source and the workflow that publishes it to gh-pages. Pushes to this branch trigger the deploy via .github/workflows/pages.yml.
0 parents  commit effd431

11 files changed

Lines changed: 1523 additions & 0 deletions

File tree

.github/workflows/pages.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Pages
2+
3+
on:
4+
push:
5+
branches:
6+
- docs
7+
workflow_dispatch:
8+
9+
# Only one Pages publish at a time; cancel any in-flight run on a new push.
10+
concurrency:
11+
group: pages
12+
cancel-in-progress: true
13+
14+
permissions:
15+
contents: write
16+
17+
jobs:
18+
deploy:
19+
name: Build and publish to gh-pages
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
25+
- name: Stage site
26+
run: |
27+
set -euo pipefail
28+
mkdir -p _site
29+
cp -R site/. _site/
30+
# Tell GitHub Pages not to run Jekyll on the output.
31+
touch _site/.nojekyll
32+
33+
- name: Publish to gh-pages branch
34+
uses: peaceiris/actions-gh-pages@v4
35+
with:
36+
github_token: ${{ secrets.GITHUB_TOKEN }}
37+
publish_dir: ./_site
38+
publish_branch: gh-pages
39+
# Keep the branch a clean snapshot of the latest site.
40+
force_orphan: true
41+
user_name: github-actions[bot]
42+
user_email: github-actions[bot]@users.noreply.github.com
43+
commit_message: "Deploy site from ${{ github.sha }}"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
_site/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Michael Fridman
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# pressly/cli docs
2+
3+
This is the long-lived source branch for the [pressly/cli](https://github.com/pressly/cli) documentation site, published at <https://pressly.github.io/cli/>.
4+
5+
It is intentionally orphaned from `main` and contains only the static site and the workflow that publishes it.
6+
7+
## Layout
8+
9+
- `site/` - static HTML/CSS/JS, served as-is.
10+
- `.github/workflows/pages.yml` - on push to `docs`, copies `site/` to a `_site/` build dir and force-pushes it to the `gh-pages` branch via [peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages).
11+
12+
GitHub Pages serves from `gh-pages`. The `docs` branch is the editable source; `gh-pages` is the deploy artifact and is overwritten on every publish.
13+
14+
## Local preview
15+
16+
```bash
17+
cd site && python3 -m http.server 8000
18+
```
19+
20+
Then open <http://localhost:8000>.

site/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# site
2+
3+
Source for the [pressly/cli docs site](https://pressly.github.io/cli/).
4+
5+
Plain HTML, CSS, and a small bit of JavaScript. No framework, no build step.
6+
The deploy workflow pushes `site/` to the `gh-pages` branch.
7+
8+
## Layout
9+
10+
- `index.html` - main page
11+
- `flagtype.html` - flagtype subpackage page
12+
- `graceful.html` - graceful subpackage page
13+
- `xflag.html` - xflag subpackage page
14+
- `styles.css` - all styling
15+
- `app.js` - copy buttons, theme toggle, heading anchors
16+
17+
## Local preview
18+
19+
```bash
20+
cd site
21+
python3 -m http.server 8000
22+
# open http://localhost:8000
23+
```
24+
25+
## Deploy
26+
27+
Pushes to the `docs` branch trigger `.github/workflows/pages.yml`, which
28+
publishes to the `gh-pages` branch. GitHub Pages must be configured to serve
29+
from that branch (Settings, Pages).

site/app.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// ---------------------------------------------------------------------------
2+
// Copy buttons on every <pre> code block (skips the API index, which is
3+
// itself a list of links rather than copy-pasteable code).
4+
// ---------------------------------------------------------------------------
5+
function initCopyButtons() {
6+
document.querySelectorAll("pre").forEach((pre) => {
7+
if (pre.classList.contains("api-index")) return;
8+
if (pre.querySelector(".copy-btn")) return;
9+
10+
const btn = document.createElement("button");
11+
btn.type = "button";
12+
btn.className = "copy-btn";
13+
btn.textContent = "copy";
14+
btn.setAttribute("aria-label", "copy code to clipboard");
15+
16+
btn.addEventListener("click", async () => {
17+
const code = pre.querySelector("code") || pre;
18+
try {
19+
await navigator.clipboard.writeText(code.textContent);
20+
btn.textContent = "copied";
21+
btn.classList.add("copied");
22+
} catch {
23+
btn.textContent = "failed";
24+
}
25+
setTimeout(() => {
26+
btn.textContent = "copy";
27+
btn.classList.remove("copied");
28+
}, 1500);
29+
});
30+
31+
pre.appendChild(btn);
32+
});
33+
}
34+
35+
// ---------------------------------------------------------------------------
36+
// Heading anchors. For every h2/h3 with an id inside <main>, append a small
37+
// "#" link. Clicking copies the absolute URL to the clipboard and gives
38+
// brief inline feedback.
39+
// ---------------------------------------------------------------------------
40+
function initHeadingAnchors() {
41+
const headings = document.querySelectorAll(
42+
"main section h2, main section h3"
43+
);
44+
headings.forEach((h) => {
45+
if (h.querySelector(".heading-anchor")) return;
46+
47+
// Prefer the heading's own id; fall back to the parent section's id
48+
// (most h2s are titled by the section's id rather than their own).
49+
let id = h.id;
50+
if (!id) {
51+
const section = h.closest("section");
52+
if (section && section.id) id = section.id;
53+
}
54+
if (!id) return;
55+
56+
const a = document.createElement("a");
57+
a.className = "heading-anchor";
58+
a.href = "#" + id;
59+
a.textContent = "#";
60+
a.setAttribute("aria-label", "copy link to section");
61+
a.title = "copy link to this section";
62+
63+
a.addEventListener("click", (e) => {
64+
e.preventDefault();
65+
const url =
66+
window.location.origin + window.location.pathname + "#" + id;
67+
history.replaceState(null, "", "#" + id);
68+
navigator.clipboard
69+
.writeText(url)
70+
.then(() => {
71+
a.classList.add("copied");
72+
setTimeout(() => a.classList.remove("copied"), 1500);
73+
})
74+
.catch(() => {});
75+
});
76+
77+
h.appendChild(a);
78+
});
79+
}
80+
81+
// ---------------------------------------------------------------------------
82+
// Theme toggle. Defaults to the OS preference; user choice is persisted in
83+
// localStorage and applied via [data-theme] on <html>. The pre-paint script
84+
// in <head> sets the attribute before first render to avoid a light->dark
85+
// flash for users who chose dark.
86+
// ---------------------------------------------------------------------------
87+
function initThemeToggle() {
88+
const btn = document.getElementById("theme-toggle");
89+
if (!btn) return;
90+
91+
const apply = (theme) => {
92+
if (theme === "dark" || theme === "light") {
93+
document.documentElement.setAttribute("data-theme", theme);
94+
} else {
95+
document.documentElement.removeAttribute("data-theme");
96+
}
97+
};
98+
99+
btn.addEventListener("click", () => {
100+
const explicit = document.documentElement.getAttribute("data-theme");
101+
const systemDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
102+
const current = explicit || (systemDark ? "dark" : "light");
103+
const next = current === "dark" ? "light" : "dark";
104+
apply(next);
105+
localStorage.setItem("theme", next);
106+
});
107+
}
108+
109+
document.addEventListener("DOMContentLoaded", () => {
110+
initCopyButtons();
111+
initThemeToggle();
112+
initHeadingAnchors();
113+
});

site/flagtype.html

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>flagtype - pressly/cli</title>
7+
<meta name="description" content="Common flag.Value implementations for pressly/cli: StringSlice, Enum, EnumDefault, StringMap, URL, Regexp." />
8+
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ctext y='14' font-size='14'%3E%E2%96%B6%3C/text%3E%3C/svg%3E" />
9+
<link rel="stylesheet" href="styles.css" />
10+
<script>
11+
(function () {
12+
try {
13+
var t = localStorage.getItem("theme");
14+
if (t === "dark" || t === "light") {
15+
document.documentElement.setAttribute("data-theme", t);
16+
}
17+
} catch (_) {}
18+
})();
19+
</script>
20+
</head>
21+
<body>
22+
23+
<header class="site-header">
24+
<div class="wrap">
25+
<a class="brand" href="./">pressly/<strong>cli</strong></a>
26+
<nav class="nav">
27+
<a href="./#quickstart">Quick start</a>
28+
<a href="./#api">API</a>
29+
<a href="./#examples">Examples</a>
30+
<div class="nav-menu">
31+
<a class="nav-menu-trigger" href="./#subpackages" aria-haspopup="true">Subpackages <span class="caret" aria-hidden="true">&#x25BC;</span></a>
32+
<div class="nav-menu-items" role="menu">
33+
<a href="flagtype.html" role="menuitem">flagtype</a>
34+
<a href="graceful.html" role="menuitem">graceful</a>
35+
<a href="xflag.html" role="menuitem">xflag</a>
36+
</div>
37+
</div>
38+
<a href="./#why">But why?</a>
39+
<span class="nav-sep" aria-hidden="true"></span>
40+
<a href="https://pkg.go.dev/github.com/pressly/cli/flagtype" target="_blank" rel="noopener">godoc</a>
41+
<a href="https://github.com/pressly/cli/tree/main/flagtype" target="_blank" rel="noopener">github</a>
42+
<button id="theme-toggle" type="button" aria-label="toggle dark mode" title="toggle dark mode">
43+
<svg class="i-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>
44+
<svg class="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
45+
</button>
46+
</nav>
47+
</div>
48+
</header>
49+
50+
<main class="wrap content" id="top">
51+
52+
<section class="intro">
53+
<h1>flagtype</h1>
54+
<p class="lede">
55+
Common <code>flag.Value</code> implementations for <code>pressly/cli</code>. Each constructor
56+
returns a value you register with <code>f.Var()</code>; storage is internal, and values are
57+
retrieved through <code>cli.GetFlag[T]</code>.
58+
</p>
59+
<pre><code class="language-go">import "github.com/pressly/cli/flagtype"</code></pre>
60+
</section>
61+
62+
<hr />
63+
64+
<section>
65+
<h2 id="index">At a glance</h2>
66+
<p>Each symbol links to its <a href="https://pkg.go.dev/github.com/pressly/cli/flagtype" target="_blank" rel="noopener">godoc</a> entry, the canonical reference.</p>
67+
<pre class="api-index"><code><span class="idx-h">FUNCTIONS</span>
68+
<a href="https://pkg.go.dev/github.com/pressly/cli/flagtype#StringSlice" target="_blank" rel="noopener">func StringSlice() flag.Value</a>
69+
<a href="https://pkg.go.dev/github.com/pressly/cli/flagtype#Enum" target="_blank" rel="noopener">func Enum(allowed ...string) flag.Value</a>
70+
<a href="https://pkg.go.dev/github.com/pressly/cli/flagtype#EnumDefault" target="_blank" rel="noopener">func EnumDefault(defaultVal string, allowed []string) flag.Value</a>
71+
<a href="https://pkg.go.dev/github.com/pressly/cli/flagtype#StringMap" target="_blank" rel="noopener">func StringMap() flag.Value</a>
72+
<a href="https://pkg.go.dev/github.com/pressly/cli/flagtype#URL" target="_blank" rel="noopener">func URL() flag.Value</a>
73+
<a href="https://pkg.go.dev/github.com/pressly/cli/flagtype#Regexp" target="_blank" rel="noopener">func Regexp() flag.Value</a></code></pre>
74+
</section>
75+
76+
<hr />
77+
78+
<section>
79+
<h2 id="patterns">Patterns</h2>
80+
<p>Register a value with <code>f.Var()</code>, retrieve it with the matching <code>cli.GetFlag[T]</code>. The full set, in one place:</p>
81+
<pre><code class="language-go">cmd := &amp;cli.Command{
82+
Name: "fetch",
83+
Flags: cli.FlagsFunc(func(f *flag.FlagSet) {
84+
f.Var(flagtype.StringSlice(),
85+
"tag", "add a tag (repeatable)")
86+
87+
f.Var(flagtype.Enum("json", "yaml", "table"),
88+
"format", "output format")
89+
90+
f.Var(flagtype.EnumDefault("sql", []string{"sql", "go"}),
91+
"type", "migration type")
92+
93+
f.Var(flagtype.StringMap(),
94+
"label", "key=value (repeatable)")
95+
96+
f.Var(flagtype.URL(),
97+
"endpoint", "API endpoint")
98+
99+
f.Var(flagtype.Regexp(),
100+
"match", "filter pattern")
101+
}),
102+
Exec: func(ctx context.Context, state *cli.State) error {
103+
tags := cli.GetFlag[[]string](state, "tag")
104+
format := cli.GetFlag[string](state, "format")
105+
migType := cli.GetFlag[string](state, "type")
106+
labels := cli.GetFlag[map[string]string](state, "label")
107+
endpoint := cli.GetFlag[*url.URL](state, "endpoint")
108+
re := cli.GetFlag[*regexp.Regexp](state, "match")
109+
110+
_, _, _, _, _, _ = tags, format, migType, labels, endpoint, re
111+
return nil
112+
},
113+
}</code></pre>
114+
<pre><code>$ fetch --help
115+
Usage:
116+
fetch [flags]
117+
118+
Flags:
119+
--endpoint url API endpoint
120+
--format enum output format
121+
--label stringMap key=value (repeatable)
122+
--match regexp filter pattern
123+
--tag stringSlice add a tag (repeatable)
124+
--type enum migration type (default: sql)</code></pre>
125+
<p>Each flagtype carries a type hint that shows up in the auto-generated help. Repeatable flags (<code>StringSlice</code>, <code>StringMap</code>) collect values across multiple appearances on the command line:</p>
126+
<pre><code class="language-bash">fetch --tag a --tag b --label env=prod --label tier=api</code></pre>
127+
</section>
128+
129+
<footer class="site-footer">
130+
<p>
131+
<a href="./">back to cli</a>
132+
&middot;
133+
<a href="https://pkg.go.dev/github.com/pressly/cli/flagtype" target="_blank" rel="noopener">pkg.go.dev/flagtype</a>
134+
</p>
135+
</footer>
136+
137+
</main>
138+
139+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/components/prism-core.min.js"></script>
140+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
141+
<script src="app.js"></script>
142+
143+
</body>
144+
</html>

0 commit comments

Comments
 (0)