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.
Files changed (2) hide show
  1. package/README.md +22 -179
  2. 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 banner" width="900"/>
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://github.com/GeiserX/ghost-github-portfolio/stargazers"><img src="https://img.shields.io/github/stars/GeiserX/ghost-github-portfolio?style=flat-square&logo=github" alt="Stars"></a>
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
- ## What it does
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
- - the repository banner, or a tile with the repository name when there is no usable banner
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
- The grid has three columns on desktop, two on tablets and one on phones. On themes built on Ghost's content grid, such as Alto, Source and Casper, the cards take the wide column and the text stays at reading width. The colours suit a dark theme.
19
+ ## Features
28
20
 
29
- [docs/preview-desktop.png](docs/preview-desktop.png) shows a real run at 1280 px wide, and [docs/preview-mobile.png](docs/preview-mobile.png) the same page on a phone. [docs/preview-config.yml](docs/preview-config.yml) is the config behind them.
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
- ## Running on a schedule
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
- Keep `config.yml` in the repository without the key, and store the key as a secret.
40
+ ## Documentation
196
41
 
197
- ## How it works
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
- 1. Fetches every public repository the user owns from the GitHub REST API. The API cannot sort by stars, so it reads all pages and sorts locally.
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.0",
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"