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
78 changes: 78 additions & 0 deletions .github/workflows/website.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Website

on:
push:
branches: [main]
paths:
- 'website/**'
- '.github/workflows/website.yml'
pull_request:
paths:
- 'website/**'
- '.github/workflows/website.yml'
workflow_dispatch:

permissions:
contents: read

jobs:
test-and-build:
runs-on: ubuntu-latest
timeout-minutes: 5
defaults:
run:
working-directory: website
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # Pinned to the existing repository workflows
- name: Set up Node.js
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: '22'
package-manager-cache: false
- name: Install Hugo v0.167.0
env:
HUGO_VERSION: '0.167.0'
HUGO_SHA256: '4d84519b9f619e6d4c3fb45a50157abeabeb724f859c60605f44c23def6e1169'
run: |
archive="hugo_${HUGO_VERSION}_linux-amd64.tar.gz"
directory="${RUNNER_TEMP}/hugo"
mkdir -p "$directory"
curl --fail --silent --show-error --location --retry 3 \
"https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/${archive}" \
--output "${directory}/${archive}"
printf '%s %s\n' "$HUGO_SHA256" "${directory}/${archive}" | sha256sum --check --strict
tar --extract --gzip --file "${directory}/${archive}" --directory "$directory" hugo
echo "$directory" >> "$GITHUB_PATH"
"${directory}/hugo" version
- name: Test
run: npm test
- name: Build
run: npm run build
- name: Upload GitHub Pages artifact
if: github.repository == 'modelpack/modctl' && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4
with:
path: website/dist

deploy:
if: github.repository == 'modelpack/modctl' && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
needs: test-and-build
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
pages: write
id-token: write
# Serialize deployments without interrupting a site that's being published.
concurrency:
group: github-pages
cancel-in-progress: false
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Configure GitHub Pages
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,6 @@ Temporary Items
# output
bin
output
website/dist/
website/.hugo_build.lock
website/resources/
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ It offers commands such as `build`, `pull`, `push`, and more, making it easy for

You can find the full documentation on the [getting started](./docs/getting-started.md).

## Website

Project website: <https://modelpack.github.io/modctl/>.

The project website lives in [`website/`](./website/README.md), built with Hugo's native multilingual support and separate English/Chinese content.
Install Hugo 0.167.0 and Node.js 22 or newer, then run locally without npm package dependencies:

```shell
cd website
npm run dev
```

Open <http://localhost:4173>. Use `npm test` to validate the site and `npm run build` to create the deployable `website/dist/` directory.

## Copyright

Copyright © contributors to ModelPack, established as ModelPack a Series of LF Projects, LLC.
Expand Down
109 changes: 109 additions & 0 deletions website/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# modctl website

The website uses **Hugo 0.167.0's native multilingual support** to generate static pages. GitHub Pages hosts only the build output. There is no client-side translation engine, custom template engine, or custom HTTP server.

## Getting started

Install the standard edition of [Hugo 0.167.0](https://github.com/gohugoio/hugo/releases/tag/v0.167.0) and add `hugo` to your PATH. The Extended edition is not required. Node.js 22+ is used only for npm commands and tests. There are no npm package dependencies, so `npm install` is unnecessary.

```sh
cd website
hugo version
npm run dev
```

Hugo's built-in development server watches files and reloads pages when they change:

- English: <http://localhost:4173/>
- Chinese: <http://localhost:4173/zh/>
- English documentation: <http://localhost:4173/getting-started/>
- Chinese documentation: <http://localhost:4173/zh/getting-started/>

You can also run Hugo directly without npm:

```sh
hugo server --bind localhost --port 4173 --baseURL http://localhost:4173/
```

The server listens on localhost by default. Website tooling does not change the Go application's dependencies.

## Directory structure

```text
website/
├── hugo.toml # Base URL, languages, and Markdown rendering
├── content/
│ ├── en/
│ │ ├── _index.md # English homepage content in front matter
│ │ └── getting-started.md # English documentation in Markdown
│ └── zh/ # Corresponding Chinese content
├── i18n/
│ ├── en.json # English UI, accessibility, and feedback messages
│ └── zh.json # Chinese translations with the same keys
├── data/commands.json # CLI examples shared across languages
├── layouts/
│ ├── baseof.html # HTML shell, language, and SEO metadata
│ ├── home.html # Shared homepage layout, without language branches
│ ├── docs/single.html # Shared documentation layout and table of contents
│ ├── _markup/ # Markdown code-block render hook with copy controls
│ └── partials/ # Shared navigation, footer, workflows, and diagrams
├── assets/artifact.svg # SVG template localized at build time
├── static/ # CSS, interaction scripts, fonts, and shared assets
└── scripts/site.test.mjs # Tests using real Hugo builds and generated output
```

### Maintaining multilingual content

- Keep page content in each language's `content` directory and short UI messages in `i18n`. Editing text should not require changes to JavaScript or the HTML layout.
- Use matching relative filenames and `translationKey` values for corresponding pages. Hugo associates the translations natively. Homepage front matter has the same structure in each language, with translated values.
- Hugo's `i18n` function renders UI messages into HTML at build time. Menu and copy controls use the labels already rendered for the current page. They do not detect a language or replace page content in the browser.
- Language navigation uses `.Translations` to generate ordinary links to the current page's translations. Content, code examples, and language links remain available with JavaScript disabled.
- The URL determines the language. English is served at the root and Chinese at `/zh/`. The site does not use browser-language redirects, cookies, or localStorage. The prototype's `?lang=zh` parameter no longer selects a language. Use `/zh/` instead.
- HTML `lang`, canonical URLs, `hreflang` links, and sitemaps are generated at build time. To add a language, update `hugo.toml`, add corresponding `content/<language>/` and `i18n/<language>.json` files, and extend the current bilingual acceptance tests.
- Keep CLI examples consistent with the repository's `docs/getting-started.md`. Illustrations and fonts are served locally. Font licenses are included in `static/assets/*-LICENSE.txt`.

### JavaScript responsibilities

`static/app.js` handles only the mobile menu, terminal tabs, clipboard controls, and table-of-contents highlighting. All localized page and terminal content is generated at build time. With JavaScript disabled, all workflow examples remain visible and inactive copy buttons are hidden.

## Testing and building

```sh
npm test
npm run build
```

Build output is written to `dist/`, including separate language pages, shared assets, and sitemaps. Hosting the generated files requires neither Node.js nor Hugo nor a backend service. Use the same Hugo version as CI, which verifies the official release archive against a pinned SHA-256 checksum.

Tests use Node's built-in test runner and invoke Hugo directly rather than maintaining a separate rendering or translation implementation. They check:

- Matching translation keys, nonempty messages, corresponding content files, and front matter structure across languages.
- Build failures for missing translations and other warnings instead of silently hiding missing translations through fallback behavior.
- Generated files, resource links and anchors under `/modctl/`, canonical URLs, and reciprocal `hreflang` links.
- Copy buttons generated from Markdown code blocks and their corresponding code element IDs.
- The absence of legacy `data-zh` attributes, client-side language-switching functions, and language storage.

If Hugo is not on your PATH, set `HUGO_BINARY=/absolute/path/to/hugo` when running tests. Development and build commands invoke `hugo` directly, without a custom installer or wrapper.

Browser regression checks should cover both pages in both languages, viewport widths from 320px to 1920px, JavaScript-disabled access, language navigation, copying, keyboard tab controls, mobile navigation, and reduced motion. Automated accessibility checks do not replace visual and maintainability reviews.

## GitHub Pages

The configured site URL is <https://modelpack.github.io/modctl/>. Chinese pages are served at <https://modelpack.github.io/modctl/zh/>.

`.github/workflows/website.yml` tests and builds pull requests. It uploads `website/dist/` and deploys only when website or workflow changes reach `main` in the upstream `modelpack/modctl` repository, or when manually triggered on `main`. Forks do not deploy automatically.

Initial setup:

1. Select **GitHub Actions** in [Settings → Pages](https://github.com/modelpack/modctl/settings/pages).
2. Merge into `main` using the repository's existing review process. Do not bypass branch protection.
3. Check results in [Actions → Website](https://github.com/modelpack/modctl/actions/workflows/website.yml). To redeploy, select **Run workflow** on `main`.
4. The `github-pages` environment permits deployments only from `main`. If environment reviewers are configured, their approval is also required.

The build job has read-only permissions. The deploy job's `pages: write` and `id-token: write` permissions are scoped to GitHub Pages deployment. No additional PAT, CNAME, or `gh-pages` branch is needed. Deployments are serialized without interrupting an in-progress deployment.

Hugo's `baseURL` is `https://modelpack.github.io/modctl/`. Templates use `.RelPermalink` and `relURL` rather than hard-coding `/modctl/`. The development command overrides this with a local URL. To use another domain or subpath, update `baseURL` and the test target.

## Design references

The design uses a cool-white, ink-blue, and blue palette with layered model-artifact illustrations. Content organization draws on Cilium, Envoy, and Harbor. Multilingual project organization follows the [Kubernetes website](https://github.com/kubernetes/website/blob/main/hugo.toml) and [Hugo's multilingual documentation](https://gohugo.io/content-management/multilingual/). No external project's page code or endorsement is reused.
49 changes: 49 additions & 0 deletions website/assets/artifact.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions website/content/en/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: "Your models. Ready for anywhere."
description: "Build, push, and pull AI models as OCI artifacts. modctl brings a familiar, open-standard workflow to model distribution."
translationKey: home
hero:
eyebrow: "OPEN SOURCE. MODEL NATIVE."
lines: ["Your models.", "Ready for", "anywhere."]
description: "Ship AI models like you ship containers. Build, push, and pull OCI model artifacts with one simple, familiar command-line tool."
why:
eyebrow: "LESS FRICTION. MORE BUILDING."
title: "Model delivery."
subtitle: "Without the detours."
description: "Your models don't need a separate universe. Bring weights, configuration, and code together, then put your existing infrastructure to work."
features:
- visual: package
title: "Many files. One artifact."
description: "Package weights, config, code, and docs into a standard OCI model artifact with a declarative Modelfile."
link: "Meet the Modelfile"
anchor: modelfile
- visual: registry
title: "Your registry. Already ready."
description: "Push models to OCI-compatible registries. Keep the tags, authentication, and distribution workflows you already know."
link: "Explore distribution"
anchor: distribute
- visual: fetch
title: "Just what you need."
description: "Fetch files by pattern or extract models straight from a remote registry, without keeping an extra local artifact copy."
link: "Work a little lighter"
anchor: extract
workflow:
eyebrow: "FROM YOUR DIRECTORY TO YOUR REGISTRY"
title: "Same workflow."
subtitle: "New possibilities."
description: "Three familiar commands. One open path from local development to wherever your models go next."
note: "Not another platform. Just a better way to ship."
steps:
- id: build
title: "Build your model"
subtitle: "From model files to an OCI artifact"
comment: "Turn a model directory into an OCI artifact"
results: ["Weights, configuration, code & docs", "Model Spec artifact layout", "Ready for your OCI registry"]
- id: push
title: "Push to your registry"
subtitle: "Package once, distribute anywhere"
comment: "Authenticate, then publish your model"
results: ["Use your existing registry credentials", "Publish a versioned model artifact", "Share a reference, not a folder of files"]
- id: pull
title: "Pull it. Make it yours."
subtitle: "Get your model where it needs to be"
comment: "Bring the model to your workspace"
results: ["Retrieve a model by its registry reference", "Extract the model files to your directory", "Continue with your own inference tooling"]
ecosystem:
eyebrow: "PART OF YOUR STACK. NOT A NEW ONE."
title: "Built to fit."
subtitle: "Not to lock you in."
description: "Built on the ModelPack Model Spec, modctl packages models as OCI-compatible artifacts. Choose where your models live, without choosing a proprietary format."
community:
eyebrow: "OPEN SOURCE. SHARED FUTURE."
title: "Your next model."
subtitle: "A better way to ship it."
description: "Start with your first artifact. Or help shape what comes next."
---
Loading
Loading