Compare commits

...

10 Commits

Author SHA1 Message Date
Tam Nguyen Duc 910619bc7a Use index .Env for the optional tap and bucket tokens (#50)
The Homebrew cask and Scoop manifest stanzas gated their token and
skip_upload on envOrDefault, which is a sprig function GoReleaser does
not define, so a real release aborted with 'function envOrDefault not
defined' the moment it reached the publish phase. goreleaser check does
not evaluate templates, so the gate slipped through until a tagged run.

Switch to index .Env "NAME", the idiom GoReleaser documents for an
optional variable: it yields the token when the secret is set (push the
update) and an empty string when it is not (skip_upload stays true).
2026-06-19 22:44:38 +07:00
Tam Nguyen Duc e0e4096f93 Pin cosign to 2.x in the release workflow (#49)
cosign 3.x flips the new bundle format on by default, which ignores the
--output-signature and --output-certificate flags our signs block passes
to sign-blob and then aborts trying to write a bundle to an empty path.
The last few releases failed at the signing step for exactly this reason.

Pin cosign-installer to v2.6.3 so the release keeps producing the
checksums.txt.sig and checksums.txt.pem pair, and so the signing tool
stops floating to a latest that can break the pipeline without warning.
2026-06-19 22:31:26 +07:00
Tam Nguyen Duc c176719031 Merge pull request #48 from tamnd/docs/install-audit
Document Homebrew and Scoop install, fix deb/rpm filenames
2026-06-19 22:17:58 +07:00
Duc-Tam Nguyen a1b82ebd1b Make the Linux repo dispatch best-effort
The release must never fail because the dispatch token is missing scope or the
call hiccups, matching how the Homebrew and Scoop steps are non-fatal.
2026-06-19 19:20:03 +07:00
Duc-Tam Nguyen 5ad8fff9df Add the Linux apt/dnf repository to install docs and releases
Documents the signed apt and dnf repository alongside Homebrew and Scoop, and
fires a repository_dispatch on release so the Linux repo rebuilds with the new
packages. The step is skipped when the dispatch token is unset.
2026-06-19 19:16:57 +07:00
Duc-Tam Nguyen 7157144c75 Document Homebrew and Scoop, and fix the deb/rpm filenames
The README never mentioned the package managers and the docs covered Homebrew
but not Scoop, even though both ship from every release. Add the Homebrew
(macOS) and Scoop (Windows) channels to both. Fix the Linux package globs:
GoReleaser names them kage_<ver>_amd64.deb and kage-<ver>-1.x86_64.rpm, not
kage_*_linux_amd64.{deb,rpm}, so the documented commands matched nothing.
2026-06-19 17:16:11 +07:00
Tam Nguyen Duc de1c0adde3 Merge pull request #47 from tamnd/chore/upgrade-actions
chore: upgrade all GitHub Actions to latest versions
2026-06-19 16:45:11 +07:00
Duc-Tam Nguyen 16bbd1b9d0 chore: upgrade all GitHub Actions to latest versions
Node.js 20 is being deprecated in the Actions runtime. Upgrade every
action to its latest release so the workflows run on Node.js 22+:

  actions/checkout        v5/v6.0.2  → v7.0.0
  browser-actions/setup-chrome  v1   → v2.1.2
  golangci/golangci-lint-action v8   → v9.2.1
  goreleaser/goreleaser-action  v6   → v7.2.2
  docker/setup-qemu-action      v3   → v4.1.0
  docker/setup-buildx-action    v3   → v4.1.0
  docker/login-action           v3   → v4.2.0
  sigstore/cosign-installer     v3   → v4.1.2
  anchore/sbom-action           v0   → v0.24.0
2026-06-19 16:44:57 +07:00
Tam Nguyen Duc 3220f6d230 Merge pull request #46 from tamnd/fix/gofmt
fix: gofmt cli/clone.go
2026-06-19 16:43:25 +07:00
Duc-Tam Nguyen 9d452ddf11 fix: gofmt cli/clone.go 2026-06-19 16:43:15 +07:00
7 changed files with 134 additions and 61 deletions
+8 -8
View File
@@ -26,7 +26,7 @@ jobs:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7.0.0
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
@@ -34,7 +34,7 @@ jobs:
cache: true
- name: install chromium (linux)
if: matrix.os == 'ubuntu-latest'
uses: browser-actions/setup-chrome@v1
uses: browser-actions/setup-chrome@v2.1.2
id: chrome
- name: gofmt
run: |
@@ -67,13 +67,13 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7.0.0
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
check-latest: true
cache: true
- uses: golangci/golangci-lint-action@v8
- uses: golangci/golangci-lint-action@v9.2.1
with:
version: latest
@@ -81,7 +81,7 @@ jobs:
govulncheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7.0.0
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
@@ -97,7 +97,7 @@ jobs:
tidy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7.0.0
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
@@ -117,7 +117,7 @@ jobs:
webview:
runs-on: macos-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7.0.0
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
@@ -133,7 +133,7 @@ jobs:
windows-gui:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7.0.0
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
+3 -3
View File
@@ -26,14 +26,14 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@v7.0.0
with:
submodules: true
# Sitemap lastmod comes from the latest content commit.
fetch-depth: 0
- name: Checkout tago
uses: actions/checkout@v6.0.2
uses: actions/checkout@v7.0.0
with:
repository: tamnd/tago
path: .tago-src
@@ -107,7 +107,7 @@ jobs:
group: cloudflare-pages-kage
cancel-in-progress: true
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/checkout@v7.0.0
with:
fetch-depth: 1
sparse-checkout: scripts/
+34 -9
View File
@@ -27,7 +27,7 @@ jobs:
if: github.ref_type != 'tag'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7.0.0
with:
fetch-depth: 0
- uses: actions/setup-go@v6
@@ -35,7 +35,7 @@ jobs:
go-version-file: go.mod
check-latest: true
cache: true
- uses: goreleaser/goreleaser-action@v6
- uses: goreleaser/goreleaser-action@v7.2.2
with:
distribution: goreleaser
version: "~> v2"
@@ -49,8 +49,10 @@ jobs:
contents: write # create the GitHub release
packages: write # push the image to ghcr.io
id-token: write # keyless cosign signing
env:
LINUX_REPO_DISPATCH_TOKEN: ${{ secrets.LINUX_REPO_DISPATCH_TOKEN }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v7.0.0
with:
fetch-depth: 0
- uses: actions/setup-go@v6
@@ -60,19 +62,25 @@ jobs:
cache: true
# Build and ship the linux/arm64 image from the amd64 runner.
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
- uses: docker/setup-qemu-action@v4.1.0
- uses: docker/setup-buildx-action@v4.1.0
- uses: docker/login-action@v4.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Tools GoReleaser shells out to for signing and SBOMs.
- uses: sigstore/cosign-installer@v3
- uses: anchore/sbom-action/download-syft@v0
# Pin cosign to the 2.x line. cosign 3.x makes the new bundle format the
# default, which ignores the --output-signature/--output-certificate flags
# the signs block uses and aborts. Pinning keeps the .sig/.pem outputs and
# stops the release tool from floating to a breaking latest.
- uses: sigstore/cosign-installer@v4.1.2
with:
cosign-release: "v2.6.3"
- uses: anchore/sbom-action/download-syft@v0.24.0
- uses: goreleaser/goreleaser-action@v6
- uses: goreleaser/goreleaser-action@v7.2.2
with:
distribution: goreleaser
version: "~> v2"
@@ -85,3 +93,20 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}
SCOOP_BUCKET_GITHUB_TOKEN: ${{ secrets.SCOOP_BUCKET_GITHUB_TOKEN }}
# Rebuild the Linux apt/dnf repository with the packages just released.
# Skipped when the dispatch token is unset, so the release never depends
# on it being configured.
- name: Refresh the Linux package repository
if: env.LINUX_REPO_DISPATCH_TOKEN != ''
run: |
code=$(curl -sS -o /dev/null -w '%{http_code}' -X POST \
-H "Authorization: Bearer $LINUX_REPO_DISPATCH_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/tamnd/linux-repo/dispatches \
-d '{"event_type":"package-released"}' || true)
if [ "$code" = "204" ]; then
echo "Linux repo refresh triggered"
else
echo "Linux repo refresh not accepted (HTTP $code); skipping"
fi
+4 -4
View File
@@ -151,11 +151,11 @@ homebrew_casks:
repository:
owner: tamnd
name: homebrew-tap
token: '{{ envOrDefault "HOMEBREW_TAP_GITHUB_TOKEN" "" }}'
token: '{{ index .Env "HOMEBREW_TAP_GITHUB_TOKEN" }}'
directory: Casks
homepage: https://github.com/tamnd/kage
description: Clone any website for offline viewing, with the JavaScript stripped out
skip_upload: '{{ if envOrDefault "HOMEBREW_TAP_GITHUB_TOKEN" "" }}false{{ else }}true{{ end }}'
skip_upload: '{{ if index .Env "HOMEBREW_TAP_GITHUB_TOKEN" }}false{{ else }}true{{ end }}'
commit_author:
name: Duc-Tam Nguyen
email: tamnd87@gmail.com
@@ -168,11 +168,11 @@ scoops:
repository:
owner: tamnd
name: scoop-bucket
token: '{{ envOrDefault "SCOOP_BUCKET_GITHUB_TOKEN" "" }}'
token: '{{ index .Env "SCOOP_BUCKET_GITHUB_TOKEN" }}'
homepage: https://github.com/tamnd/kage
description: Clone any website for offline viewing, with the JavaScript stripped out
license: MIT
skip_upload: '{{ if envOrDefault "SCOOP_BUCKET_GITHUB_TOKEN" "" }}false{{ else }}true{{ end }}'
skip_upload: '{{ if index .Env "SCOOP_BUCKET_GITHUB_TOKEN" }}false{{ else }}true{{ end }}'
commit_author:
name: Duc-Tam Nguyen
email: tamnd87@gmail.com
+21 -1
View File
@@ -24,7 +24,27 @@ Full docs and guides live at **[kage.tamnd.com](https://kage.tamnd.com)**.
go install github.com/tamnd/kage/cmd/kage@latest
```
Prefer a prebuilt binary? Grab an archive, a `.deb`/`.rpm`/`.apk`, or a checksum from [releases](https://github.com/tamnd/kage/releases). Or skip installing Chrome yourself and use the container image, which bundles Chromium:
Prefer a prebuilt binary? Grab an archive, a `.deb`/`.rpm`/`.apk`, or a checksum from [releases](https://github.com/tamnd/kage/releases). Or let a package manager handle it:
```bash
# Homebrew (macOS)
brew install tamnd/tap/kage
# Scoop (Windows)
scoop bucket add tamnd https://github.com/tamnd/scoop-bucket
scoop install kage
# apt (Debian, Ubuntu)
curl -fsSL https://tamnd.github.io/linux-repo/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/tamnd.gpg
echo "deb [signed-by=/usr/share/keyrings/tamnd.gpg] https://tamnd.github.io/linux-repo/apt stable main" | sudo tee /etc/apt/sources.list.d/tamnd.list
sudo apt update && sudo apt install kage
# dnf (Fedora, RHEL)
sudo dnf config-manager --add-repo https://tamnd.github.io/linux-repo/dnf/tamnd.repo
sudo dnf install kage
```
Or skip installing Chrome yourself and use the container image, which bundles Chromium:
```bash
docker run --rm -v "$PWD/out:/out" ghcr.io/tamnd/kage clone paulgraham.com
+31 -31
View File
@@ -16,37 +16,37 @@ import (
// cloneFlags holds the parsed flag values for one invocation.
type cloneFlags struct {
out string
reserved string
workers int
assetWorkers int
browserPages int
maxPages int
maxDepth int
traversal string
maxAssetMB int64
keepMedia bool
skipExt []string
allAssetHosts bool
timeout time.Duration
settle time.Duration
renderTO time.Duration
scroll bool
userAgent string
subdomains bool
scopePrefix string
exclude []string
noRobots bool
noSitemap bool
headful bool
keepNoscript bool
mobileReadable bool
chromeBin string
controlURL string
noResume bool
refresh bool
force bool
quiet bool
out string
reserved string
workers int
assetWorkers int
browserPages int
maxPages int
maxDepth int
traversal string
maxAssetMB int64
keepMedia bool
skipExt []string
allAssetHosts bool
timeout time.Duration
settle time.Duration
renderTO time.Duration
scroll bool
userAgent string
subdomains bool
scopePrefix string
exclude []string
noRobots bool
noSitemap bool
headful bool
keepNoscript bool
mobileReadable bool
chromeBin string
controlURL string
noResume bool
refresh bool
force bool
quiet bool
}
func newCloneCmd() *cobra.Command {
+33 -5
View File
@@ -1,6 +1,6 @@
---
title: "Installation"
description: "Install kage from Go, Homebrew, a release archive, a Linux package, or the container image, and point it at a browser."
description: "Install kage from Go, Homebrew, Scoop, a release archive, a Linux package, or the container image, and point it at a browser."
weight: 20
---
@@ -12,26 +12,54 @@ kage is a single binary. Pick whichever channel suits you.
go install github.com/tamnd/kage/cmd/kage@latest
```
## Homebrew
## Homebrew (macOS)
```bash
brew install tamnd/tap/kage
```
The cask installs the prebuilt macOS binary. On Linux, use the packages below or
`go install`.
## Scoop (Windows)
```bash
scoop bucket add tamnd https://github.com/tamnd/scoop-bucket
scoop install kage
```
## Linux (apt and dnf)
A signed apt and dnf repository tracks every release, so `apt upgrade` and
`dnf upgrade` keep kage current.
```bash
# Debian, Ubuntu
curl -fsSL https://tamnd.github.io/linux-repo/gpg.key \
| sudo gpg --dearmor -o /usr/share/keyrings/tamnd.gpg
echo "deb [signed-by=/usr/share/keyrings/tamnd.gpg] https://tamnd.github.io/linux-repo/apt stable main" \
| sudo tee /etc/apt/sources.list.d/tamnd.list
sudo apt update && sudo apt install kage
# Fedora, RHEL
sudo dnf config-manager --add-repo https://tamnd.github.io/linux-repo/dnf/tamnd.repo
sudo dnf install kage
```
## Release archives and Linux packages
Every [release](https://github.com/tamnd/kage/releases) attaches `tar.gz`
archives (and a `.zip` for Windows) for Linux, macOS, Windows, and FreeBSD, plus
`.deb`, `.rpm`, and `.apk` packages and a `checksums.txt` with a cosign
signature. Download the one for your platform, extract `kage`, and put it on your
`PATH`.
`PATH`. To install a package directly without the repo above:
```bash
# Debian/Ubuntu
sudo dpkg -i kage_*_linux_amd64.deb
sudo dpkg -i kage_*_amd64.deb
# Fedora/RHEL
sudo rpm -i kage_*_linux_amd64.rpm
sudo rpm -i kage-*.x86_64.rpm
```
## Container