ghost-github-portfolio 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -179
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,208 +1,51 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="docs/images/banner.svg" alt="Ghost GitHub Portfolio
|
|
2
|
+
<img src="https://raw.githubusercontent.com/GeiserX/ghost-github-portfolio/main/docs/images/banner.svg" alt="Ghost GitHub Portfolio" width="900"/>
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="https://www.npmjs.com/package/ghost-github-portfolio"><img src="https://img.shields.io/npm/v/ghost-github-portfolio?style=flat-square&logo=npm" alt="npm"></a>
|
|
7
|
-
<a href="https://hub.docker.com/r/drumsergio/ghost-github-portfolio"><img src="https://img.shields.io/docker/pulls/drumsergio/ghost-github-portfolio?style=flat-square&logo=docker" alt="Docker Pulls"></a>
|
|
8
7
|
<a href="https://github.com/GeiserX/ghost-github-portfolio/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/GeiserX/ghost-github-portfolio/ci.yml?style=flat-square&logo=github&label=CI" alt="CI"></a>
|
|
9
|
-
<a href="LICENSE"><img src="https://img.shields.io/github/license/GeiserX/ghost-github-portfolio?style=flat-square" alt="License"></a>
|
|
10
|
-
<a href="https://
|
|
8
|
+
<a href="https://github.com/GeiserX/ghost-github-portfolio/blob/main/LICENSE"><img src="https://img.shields.io/github/license/GeiserX/ghost-github-portfolio?style=flat-square" alt="License"></a>
|
|
9
|
+
<a href="https://hub.docker.com/r/drumsergio/ghost-github-portfolio"><img src="https://img.shields.io/docker/pulls/drumsergio/ghost-github-portfolio?style=flat-square&logo=docker" alt="Docker Pulls"></a>
|
|
11
10
|
<a href="https://codecov.io/gh/GeiserX/ghost-github-portfolio"><img src="https://codecov.io/gh/GeiserX/ghost-github-portfolio/graph/badge.svg" alt="codecov"></a>
|
|
12
11
|
</p>
|
|
13
12
|
|
|
14
13
|
<p align="center"><strong>Keep a Ghost portfolio page in step with your GitHub repositories.</strong></p>
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
`ghost-github-portfolio` reads your public GitHub repositories, keeps the most starred ones, and writes them to a Ghost page as a grid of cards through the Ghost Admin API. Run it on a schedule and the page follows your star counts.
|
|
19
|
-
|
|
20
|
-
The page is one Ghost html card with its own scoped style. It holds a short intro, then a card per repository, then one line that points at your GitHub profile. Each card has:
|
|
15
|
+
`ghost-github-portfolio` reads your public GitHub repositories, keeps the most starred ones, and writes them to a Ghost page as a grid of cards through the Ghost Admin API. Run it as a CLI, in Docker, or as a GitHub Action on a schedule, and the page follows your star counts.
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
- the name, linked to GitHub, and the star count as plain text, such as "215 stars"
|
|
24
|
-
- one line on what the repository does
|
|
25
|
-
- a "Site" link when the repository has a homepage outside github.com
|
|
17
|
+
<p align="center"><img src="https://raw.githubusercontent.com/GeiserX/ghost-github-portfolio/main/docs/images/screenshots/preview-desktop.png" alt="Portfolio page preview" width="900"/></p>
|
|
26
18
|
|
|
27
|
-
|
|
19
|
+
## Features
|
|
28
20
|
|
|
29
|
-
|
|
21
|
+
- One Ghost html card with its own scoped style: an intro, a card per repository, and a line pointing at your GitHub profile.
|
|
22
|
+
- Each card has the repository banner (or a name tile), the name and star count, one line on what it does, and a "Site" link.
|
|
23
|
+
- Three columns on desktop, two on tablets, one on phones; wide cards on Alto, Source and Casper.
|
|
24
|
+
- Filters by stars, forks, archived, name patterns and awesome lists, then cuts to `maxRepos`.
|
|
25
|
+
- Per-repo overrides in `config.yml` or in a `.ghost-portfolio.yml` inside each repository.
|
|
26
|
+
- Checks that each SVG banner is self-contained, and warns when it falls back to a name tile.
|
|
27
|
+
- Can write through the Ghost origin when a firewall blocks `<img` in request bodies.
|
|
28
|
+
- `--dry-run`, `--json` and `--html-out` previews that never touch Ghost.
|
|
30
29
|
|
|
31
30
|
## Quick start
|
|
32
31
|
|
|
33
|
-
Install it, or run it with `npx`:
|
|
34
|
-
|
|
35
32
|
```bash
|
|
36
33
|
npm install -g ghost-github-portfolio
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Write an example config and edit it:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
34
|
ghost-github-portfolio init
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Get an Admin API key in Ghost under Settings, Integrations, "Add custom integration". It looks like `KEY_ID:SECRET`.
|
|
46
|
-
|
|
47
|
-
Preview the page, then sync it:
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
# Builds the page and writes a standalone preview. Ghost is not touched
|
|
51
35
|
ghost-github-portfolio sync --config config.yml --html-out preview.html
|
|
52
|
-
|
|
53
|
-
# Writes the page to Ghost
|
|
54
|
-
GHOST_ADMIN_API_KEY=KEY_ID:SECRET ghost-github-portfolio sync --config config.yml --verbose
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
`--dry-run` builds the page and lists each card without writing anything. `--json` prints the Ghost document instead. Both write the html card to `/tmp/ghost-portfolio-preview.html`. `--html-out <file>` also wraps it in a dark page that approximates Ghost's Alto theme, so you can open it in a browser.
|
|
58
|
-
|
|
59
|
-
## Configuration
|
|
60
|
-
|
|
61
|
-
```yaml
|
|
62
|
-
github:
|
|
63
|
-
username: YOUR_GITHUB_USERNAME
|
|
64
|
-
# token: ghp_xxx # Optional, for higher rate limits (env: GHOST_GITHUB_TOKEN)
|
|
65
|
-
excludePatterns: # Regular expressions matched against repo names
|
|
66
|
-
- "^homebrew-"
|
|
67
|
-
|
|
68
|
-
ghost:
|
|
69
|
-
url: https://your-ghost-blog.com
|
|
70
|
-
adminApiKey: "KEY_ID:SECRET_HEX" # env GHOST_ADMIN_API_KEY wins over this
|
|
71
|
-
pageSlug: portfolio # Or pageId with the page's hex ID
|
|
72
|
-
# originUrl: http://ghost:2368 # See "Writing through the Ghost origin"
|
|
73
|
-
# hostHeader: your-ghost-blog.com
|
|
74
|
-
|
|
75
|
-
portfolio:
|
|
76
|
-
minStars: 2 # Only repos with at least this many stars
|
|
77
|
-
maxRepos: 20 # How many cards, counted after every filter
|
|
78
|
-
columns: 3 # Cards per row on desktop, from 1 to 6
|
|
79
|
-
includeForked: false
|
|
80
|
-
includeArchived: false
|
|
81
|
-
excludeAwesomeLists: true # Skip repos named "awesome*" or tagged "awesome-list"
|
|
82
|
-
showBanner: true
|
|
83
|
-
defaultBannerPath: docs/images/banner.svg
|
|
84
|
-
intro: "I build things nobody asked for, one repo at a time. These are the ones that got the most stars on GitHub, with a line on what each one does."
|
|
85
|
-
|
|
86
|
-
bannerPaths: # Banner path per repo, tried before defaultBannerPath
|
|
87
|
-
my-project: media/banner.svg
|
|
88
|
-
|
|
89
|
-
excludeRepos:
|
|
90
|
-
- .github
|
|
91
|
-
|
|
92
|
-
repos: # Per-repo overrides
|
|
93
|
-
my-project:
|
|
94
|
-
description: "One line that replaces the GitHub description"
|
|
95
|
-
homepage: https://example.com/guide/ # Replaces the GitHub homepage field
|
|
96
|
-
siteLabel: Guide # Link text, "Site" by default
|
|
97
|
-
bannerPath: assets/banner.png
|
|
98
|
-
exclude: false
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
Filters run in this order: minimum stars, forks, archived, `excludeRepos`, per-repo `exclude`, `excludePatterns`, awesome lists. The tool sorts what is left by stars and only then cuts it to `maxRepos`, so an excluded repository never takes a slot.
|
|
102
|
-
|
|
103
|
-
A repository can also carry its own `.ghost-portfolio.yml` at the root of its default branch with `description`, `homepage`, `siteLabel` and `bannerPath`. Values in your `config.yml` win over it.
|
|
104
|
-
|
|
105
|
-
| Variable | Meaning |
|
|
106
|
-
|---|---|
|
|
107
|
-
| `GHOST_ADMIN_API_KEY` | Ghost Admin API key. Overrides `ghost.adminApiKey`, which can then be left out. |
|
|
108
|
-
| `GHOST_GITHUB_TOKEN` | GitHub token for higher rate limits. Used when `github.token` is not set. The generic `GITHUB_TOKEN` is ignored on purpose. |
|
|
109
|
-
|
|
110
|
-
## Descriptions and site links
|
|
111
|
-
|
|
112
|
-
A `description` in `config.yml` is used exactly as written, so that is the place for one-liners you want to keep by hand. Otherwise the tool takes the GitHub description, or the one in `.ghost-portfolio.yml`, and cleans it up:
|
|
113
|
-
|
|
114
|
-
- it keeps the first sentence only
|
|
115
|
-
- em and en dashes become commas
|
|
116
|
-
- emoji and `:shortcodes:` are removed
|
|
117
|
-
- it is cut to 140 characters at a word boundary
|
|
118
|
-
|
|
119
|
-
The site link comes from `homepage` in the overrides, else from the repository's homepage field. A homepage on github.com is skipped, since the name already links there.
|
|
120
|
-
|
|
121
|
-
## Banners
|
|
122
|
-
|
|
123
|
-
For each repository the tool fetches the per-repo banner path if you set one, then `defaultBannerPath`, from `raw.githubusercontent.com` on the default branch. It uses the first one that passes both checks:
|
|
124
|
-
|
|
125
|
-
1. It answers HTTP 200.
|
|
126
|
-
2. If it is an SVG, it is self-contained. Every `href`, `xlink:href`, `url(...)` and `@import` must point at a `#id` inside the file or at a `data:` URI. Browsers never load other files referenced by an SVG that is shown through `<img>`, so a banner that pulls in `logo.png` or a web font comes out blank or broken on the page.
|
|
127
|
-
|
|
128
|
-
When no path passes, the card gets a tile with the repository name, and the run prints a warning that names the reason so you can fix the source:
|
|
129
|
-
|
|
130
36
|
```
|
|
131
|
-
Warning: my-project gets a name tile: docs/images/banner.svg: not self-contained, it loads logo.png
|
|
132
|
-
Warning: other-project gets a name tile: docs/images/banner.svg: missing (HTTP 404)
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
To make an SVG self-contained, inline the images it references as base64 `data:` URIs, or redraw them as SVG shapes. The card shows the banner in a 9:2 box and crops it around the centre, so a 900x200 banner fits exactly.
|
|
136
|
-
|
|
137
|
-
## Writing through the Ghost origin
|
|
138
|
-
|
|
139
|
-
Some setups put a web application firewall in front of the public Ghost URL that rejects request bodies containing `<img`, which every portfolio update does. Set `ghost.originUrl` to reach Ghost directly instead, for example on the local network:
|
|
140
|
-
|
|
141
|
-
```yaml
|
|
142
|
-
ghost:
|
|
143
|
-
url: https://your-ghost-blog.com
|
|
144
|
-
originUrl: http://ghost-host:2368
|
|
145
|
-
hostHeader: your-ghost-blog.com # Defaults to the host of url
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Admin API requests then go to `originUrl` with `Host: <hostHeader>` and `X-Forwarded-Proto: https`, so Ghost answers as it does for the public site. The token audience stays `/admin/`. The tool does not follow redirects, because a redirect would lead back through the public URL. Without `originUrl` the requests go to `url` as before.
|
|
149
37
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
The Docker image runs one sync and exits. To keep it running, loop inside the container and let Docker restart it after a reboot:
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
docker run -d --name ghost-portfolio --restart unless-stopped \
|
|
156
|
-
-v /path/to/config.yml:/config/config.yml:ro \
|
|
157
|
-
--env-file /path/to/portfolio.env \
|
|
158
|
-
--entrypoint sh \
|
|
159
|
-
drumsergio/ghost-github-portfolio:0.4.0 \
|
|
160
|
-
-c 'while true; do node dist/index.js sync --config /config/config.yml; sleep 86400; done'
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
`portfolio.env` holds `GHOST_ADMIN_API_KEY=KEY_ID:SECRET` and, if you want it, `GHOST_GITHUB_TOKEN`.
|
|
164
|
-
|
|
165
|
-
Or run it from the host's cron, once a day at 06:00:
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
0 6 * * * docker run --rm -v /path/to/config.yml:/config/config.yml:ro --env-file /path/to/portfolio.env drumsergio/ghost-github-portfolio:0.4.0
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
A GitHub Actions schedule works too, as long as the runner can reach your Ghost URL. A GitHub-hosted runner cannot reach an `originUrl` on a private network.
|
|
172
|
-
|
|
173
|
-
```yaml
|
|
174
|
-
name: Update portfolio
|
|
175
|
-
|
|
176
|
-
on:
|
|
177
|
-
schedule:
|
|
178
|
-
- cron: "0 6 * * *"
|
|
179
|
-
workflow_dispatch:
|
|
180
|
-
|
|
181
|
-
jobs:
|
|
182
|
-
sync:
|
|
183
|
-
runs-on: ubuntu-latest
|
|
184
|
-
steps:
|
|
185
|
-
- uses: actions/checkout@v4
|
|
186
|
-
- uses: actions/setup-node@v4
|
|
187
|
-
with:
|
|
188
|
-
node-version: 22
|
|
189
|
-
- run: npx ghost-github-portfolio@0.4.0 sync --config config.yml --verbose
|
|
190
|
-
env:
|
|
191
|
-
GHOST_ADMIN_API_KEY: ${{ secrets.GHOST_ADMIN_API_KEY }}
|
|
192
|
-
GHOST_GITHUB_TOKEN: ${{ github.token }}
|
|
193
|
-
```
|
|
38
|
+
Open `preview.html` in a browser; Ghost is not touched. When the preview looks right, drop `--html-out` and set `GHOST_ADMIN_API_KEY` (Ghost, Settings, Integrations, "Add custom integration") to write the page. [Usage](https://github.com/GeiserX/ghost-github-portfolio/blob/main/docs/usage.md) has the other install paths.
|
|
194
39
|
|
|
195
|
-
|
|
40
|
+
## Documentation
|
|
196
41
|
|
|
197
|
-
|
|
42
|
+
- [Configuration](https://github.com/GeiserX/ghost-github-portfolio/blob/main/docs/configuration.md): `config.yml`, filters, environment variables, descriptions, banners, writing through the Ghost origin
|
|
43
|
+
- [Usage](https://github.com/GeiserX/ghost-github-portfolio/blob/main/docs/usage.md): install, init, preview and sync
|
|
44
|
+
- [How it works](https://github.com/GeiserX/ghost-github-portfolio/blob/main/docs/how-it-works.md): the page layout and the sync steps
|
|
45
|
+
- [Running on a schedule](https://github.com/GeiserX/ghost-github-portfolio/blob/main/docs/scheduling.md): the GitHub Action, cron and a Docker loop
|
|
198
46
|
|
|
199
|
-
|
|
200
|
-
2. Applies the filters, sorts by stars and keeps `maxRepos`.
|
|
201
|
-
3. Reads each repository's `.ghost-portfolio.yml`, if any.
|
|
202
|
-
4. Checks each banner as described above.
|
|
203
|
-
5. Builds one html card and wraps it in the Ghost lexical document format.
|
|
204
|
-
6. Fetches the page to get its `updated_at`, then writes the new content through the Admin API with a short-lived JWT.
|
|
47
|
+
The roadmap is in [docs/ROADMAP.md](https://github.com/GeiserX/ghost-github-portfolio/blob/main/docs/ROADMAP.md).
|
|
205
48
|
|
|
206
49
|
## License
|
|
207
50
|
|
|
208
|
-
[GPL-3.0](LICENSE)
|
|
51
|
+
[GPL-3.0-or-later](https://github.com/GeiserX/ghost-github-portfolio/blob/main/LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghost-github-portfolio",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Write your most starred GitHub repositories to a Ghost page as a grid of cards, through the Ghost Admin API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"self-hosted"
|
|
35
35
|
],
|
|
36
36
|
"author": "GeiserX",
|
|
37
|
-
"license": "GPL-3.0",
|
|
37
|
+
"license": "GPL-3.0-or-later",
|
|
38
38
|
"repository": {
|
|
39
39
|
"type": "git",
|
|
40
40
|
"url": "https://github.com/GeiserX/ghost-github-portfolio.git"
|