Skip to content

Commit 4f991e0

Browse files
committed
chore: prepare verified GitHub Pages deployment
Signed-off-by: Jeremi Joslin <jeremi@joslin.fr>
1 parent c5f7aa3 commit 4f991e0

7 files changed

Lines changed: 114 additions & 6 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,28 @@ jobs:
6262
path: test-results/
6363
if-no-files-found: ignore
6464
retention-days: 7
65+
- name: Upload GitHub Pages artifact
66+
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && vars.PAGES_DEPLOYMENT_ENABLED == 'true'
67+
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
68+
with:
69+
path: dist/
70+
71+
deploy:
72+
name: Deploy to GitHub Pages
73+
needs: check
74+
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' && vars.PAGES_DEPLOYMENT_ENABLED == 'true'
75+
runs-on: ubuntu-24.04
76+
timeout-minutes: 10
77+
permissions:
78+
pages: write
79+
id-token: write
80+
environment:
81+
name: github-pages
82+
url: ${{ steps.deployment.outputs.page_url }}
83+
concurrency:
84+
group: github-pages
85+
cancel-in-progress: false
86+
steps:
87+
- name: Deploy verified site
88+
id: deployment
89+
uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1

‎AGENTS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Registry Stack Marketplace
22

33
Separate Astro static marketplace for marketplace.registrystack.org.
4-
PR preview deployment is authorized. Production deployment stays disabled until Jeremi explicitly authorizes it.
4+
Regular GitHub Pages deployment is authorized. Resolve the repository visibility or plan prerequisite before enabling PAGES_DEPLOYMENT_ENABLED. Never change repository visibility without Jeremi’s explicit approval.
55

66
- npm run check:ci validates a clean checkout without private assets.
77
- npm run check adds explicit local private-archive verification.

‎README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Registry Stack Marketplace
22

3-
Astro static site for marketplace.registrystack.org, kept local and unpublished for review. The source lives in the private registrystack/marketplace repository. Production hosting, domain changes, and production deployment remain disabled.
3+
Astro static site for marketplace.registrystack.org, kept local and unpublished for review. The source lives in the private registrystack/marketplace repository. Regular GitHub Pages deployment is prepared, with activation pending the repository visibility or plan prerequisite.
44

55
## Review locally
66

@@ -75,7 +75,7 @@ For compatibility with the existing native request form, requests use request_ty
7575

7676
## Repository and CI
7777

78-
The repository is private at https://github.com/registrystack/marketplace. Marketplace CI runs on pull requests, pushes to main, and manual dispatch. Its token is read-only; it has no production deployment job, Pages write permission, production domain binding, or production secret configuration. Official actions are pinned to immutable commits.
78+
The repository is private at https://github.com/registrystack/marketplace. Marketplace CI runs on pull requests, pushes to main, and manual dispatch. Its build token is read-only. A separate Pages deployment job has the Pages and OIDC permissions required for publication; it runs only after successful checks on main and when PAGES_DEPLOYMENT_ENABLED is exactly true. Official actions are pinned to immutable commits.
7979

8080
The workflow installs locked dependencies, runs check:ci, executes browser journeys on a temporary loopback server, and checks the fully intercepted request/analytics funnel. It uploads only the verified dist/ site and browser review images, retained for seven days. Preview output rejects ZIPs and other archives, private asset paths, live request/analytics configuration, and indexable HTML.
8181

@@ -85,4 +85,4 @@ The tracking protocol and shared client are pinned by data/tracking-contract.jso
8585

8686
Those pins describe the prepared protocol-v3 integration. They do not claim that the main website and documentation have been deployed with it.
8787

88-
PR preview hosting is configured separately from these CI artifacts. A built artifact is not a deployed preview URL. Production remains disabled while the preview hosting account is selected and connected.
88+
The selected host is regular GitHub Pages. Pull requests produce CI review artifacts; main can publish the checked Pages artifact after explicit activation. GitHub currently refuses Pages activation for this private repository on the organization plan. See docs/github-pages.md for activation steps and the Cloudflare DNS configuration. No separate PR hosting service is required.

‎docs/cloudflare-dns.json‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "CNAME",
3+
"name": "marketplace.registrystack.org",
4+
"content": "registrystack.github.io",
5+
"ttl": 1,
6+
"proxied": false
7+
}

‎docs/github-pages.md‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# GitHub Pages and Cloudflare DNS
2+
3+
The marketplace uses the regular GitHub Pages deployment from the checked main branch. CI still runs on every pull request. It publishes only after checks pass, from main, and when the repository variable PAGES_DEPLOYMENT_ENABLED is exactly true. Pull requests never deploy to the production Pages environment.
4+
5+
## Current activation prerequisite
6+
7+
The repository was created private. GitHub rejected Pages activation because the current organization plan does not support Pages for this private repository. The repository must either become public with the owner's explicit approval, or the organization must use a GitHub plan that supports private-repository Pages. Changing source visibility and changing website visibility are different actions; a public website does not require its source to be public on a suitable plan.
8+
9+
No private archive bytes are tracked or uploaded. The 87 request-only ZIPs remain in ignored content/downloads/. Only static catalogue pages, CSS, and browser scripts enter the checked deployment artifact.
10+
11+
## DNS configuration
12+
13+
In the registrystack.org Cloudflare zone:
14+
15+
| Setting | Value |
16+
| --- | --- |
17+
| Type | CNAME |
18+
| Name | marketplace |
19+
| Target | registrystack.github.io |
20+
| Proxy status | DNS only, grey cloud |
21+
| TTL | Auto |
22+
23+
The equivalent Cloudflare API request body is in [cloudflare-dns.json](cloudflare-dns.json). TTL 1 represents Auto. The CNAME target is the organization hostname, without a repository path.
24+
25+
Because this record is DNS only, GitHub serves the TLS certificate. Cloudflare's proxy SSL mode does not control this hostname.
26+
27+
## GitHub activation
28+
29+
After resolving the repository plan or visibility prerequisite:
30+
31+
1. In repository Settings, Pages, select GitHub Actions as the deployment source.
32+
2. Set the custom domain to marketplace.registrystack.org. A CNAME file in this repository is not required by the Actions publishing method.
33+
3. Confirm DNS points to registrystack.github.io and enable Enforce HTTPS when GitHub has issued the certificate. Certificate provisioning can take up to 24 hours.
34+
4. Set the repository variable PAGES_DEPLOYMENT_ENABLED to true.
35+
5. Run Marketplace CI on main, or push a main-branch change. The deploy job consumes the same artifact that passed the checks.
36+
37+
Setting the activation variable to any other value disables publication. GitHub Pages must be enabled first; the flag does not bypass a GitHub plan restriction.
38+
39+
The initial deployment retains noindex metadata and disallow-all robots.txt while the content remains under review. Form submissions and analytics remain unconfigured. Removing those review controls and enabling live integrations require a deliberate subsequent change.
40+
41+
## Sources
42+
43+
- [GitHub Pages availability](https://docs.github.com/en/pages/getting-started-with-github-pages/what-is-github-pages)
44+
- [GitHub custom domain setup](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site)
45+
- [GitHub Pages HTTPS](https://docs.github.com/en/pages/getting-started-with-github-pages/securing-your-github-pages-site-with-https)
46+
- [Cloudflare DNS proxy status](https://developers.cloudflare.com/dns/proxy-status/)

‎tests/ci-workflow.test.mjs‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,30 @@ import assert from 'node:assert/strict';
22
import { readFileSync } from 'node:fs';
33
import test from 'node:test';
44

5-
test('CI checks the archive-free site and publishes only a checked review artifact', () => {
5+
test('CI checks the archive-free site with read-only build permissions', () => {
66
const workflow = readFileSync(new URL('../.github/workflows/ci.yml', import.meta.url), 'utf8');
77
assert.match(workflow, /pull_request:/);
88
assert.match(workflow, /contents: read/);
99
assert.match(workflow, /persist-credentials: false/);
1010
assert.match(workflow, /runs-on: ubuntu-24\.04/);
1111
for (const check of ['npm ci', 'npm run check:ci', 'node scripts/check-browser.mjs', 'npm run test:funnel', 'node scripts/verify-preview-artifact.mjs']) assert.ok(workflow.includes(check), check);
1212
assert.match(workflow, /path: dist\//);
13-
assert.doesNotMatch(workflow, /pull_request_target|pages: write|id-token: write|deploy-pages|--prod|content\/downloads/);
13+
const checkJob = workflow.split('\n check:')[1].split('\n deploy:')[0];
14+
assert.doesNotMatch(checkJob, /pages: write|id-token: write|deploy-pages/);
15+
assert.doesNotMatch(workflow, /pull_request_target|--prod|content\/downloads/);
1416
for (const action of workflow.matchAll(/uses: (\S+)/g)) assert.match(action[1], /@[0-9a-f]{40}$/);
1517
});
18+
19+
test('regular Pages deployment requires passing CI, main, and explicit activation', () => {
20+
const workflow = readFileSync(new URL('../.github/workflows/ci.yml', import.meta.url), 'utf8');
21+
const deploy = workflow.split('\n deploy:')[1];
22+
assert.ok(deploy, 'Pages deployment job must be prepared.');
23+
assert.match(deploy, /needs: check/);
24+
assert.match(deploy, /github\.ref == 'refs\/heads\/main'/);
25+
assert.match(deploy, /github\.event_name != 'pull_request'/);
26+
assert.match(deploy, /vars\.PAGES_DEPLOYMENT_ENABLED == 'true'/);
27+
assert.match(deploy, /pages: write/);
28+
assert.match(deploy, /id-token: write/);
29+
assert.match(deploy, /name: github-pages/);
30+
assert.match(deploy, /actions\/deploy-pages@[0-9a-f]{40}/);
31+
});

‎tests/cloudflare-config.test.mjs‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import assert from 'node:assert/strict';
2+
import { readFileSync } from 'node:fs';
3+
import test from 'node:test';
4+
5+
test('Cloudflare configuration points the marketplace subdomain directly to GitHub Pages', () => {
6+
const record = JSON.parse(readFileSync(new URL('../docs/cloudflare-dns.json', import.meta.url), 'utf8'));
7+
assert.deepEqual(record, {
8+
type: 'CNAME',
9+
name: 'marketplace.registrystack.org',
10+
content: 'registrystack.github.io',
11+
ttl: 1,
12+
proxied: false,
13+
});
14+
});

0 commit comments

Comments
 (0)