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
8 changes: 6 additions & 2 deletions .github/workflows/build-registry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ jobs:
with:
node-version: 'lts/*'

- run: npm ci || npm install --no-save
- uses: pnpm/action-setup@v4
with:
version: 11.5.1

- run: pnpm install --frozen-lockfile

- name: Build registry index
run: npx tsx scripts/build-registry.ts
run: pnpm exec tsx scripts/build-registry.ts

- name: Commit if changed
run: |
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/ci-feedback-loop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,24 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- run: npm ci || npm install
- uses: pnpm/action-setup@v4
with:
version: 11.5.1
- run: pnpm install --frozen-lockfile
continue-on-error: true

- name: Lint
run: npm run lint --if-present 2>&1
run: pnpm run lint 2>&1
continue-on-error: true
id: lint

- name: Type check
run: npx tsc --noEmit --skipLibCheck 2>&1
run: pnpm exec tsc --noEmit --skipLibCheck 2>&1
continue-on-error: true
id: typecheck

- name: Test
run: npm test --if-present 2>&1
run: pnpm test 2>&1
continue-on-error: true
id: test

Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/host-effects-contract.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- run: npm ci || npm install --no-save
- run: npm run gate:host-effects
- uses: pnpm/action-setup@v4
with:
version: 11.5.1
- run: pnpm install --frozen-lockfile
- run: pnpm run gate:host-effects
18 changes: 12 additions & 6 deletions .github/workflows/native-extension-catalog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- run: npm ci || npm install --no-save
- run: npm run gate:native-extension-catalog
- run: npm run test:native-extension-catalog
- uses: pnpm/action-setup@v4
with:
version: 11.5.1
- run: pnpm install --frozen-lockfile
- run: pnpm run gate:native-extension-catalog
- run: pnpm run test:native-extension-catalog

promote:
if: github.event_name == 'workflow_dispatch'
Expand All @@ -65,7 +68,10 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- run: npm ci || npm install --no-save
- uses: pnpm/action-setup@v4
with:
version: 11.5.1
- run: pnpm install --frozen-lockfile

- name: Mint a GitHub App installation token
id: app-token
Expand Down Expand Up @@ -147,9 +153,9 @@ jobs:
"source_revision": "${SOURCE_REVISION}"
}
EOF
npx tsx scripts/publish-native-extension-catalog.ts \
pnpm exec tsx scripts/publish-native-extension-catalog.ts \
registry/native-extensions/v1/index.json "$RUNNER_TEMP/native-release.json"
npm run gate:native-extension-catalog
pnpm run gate:native-extension-catalog

- name: Open reviewed catalog promotion PR
shell: bash
Expand Down
27 changes: 17 additions & 10 deletions .github/workflows/plugin-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,27 +45,31 @@ jobs:
with:
node-version: 'lts/*'

- run: npm ci || npm install --no-save
- uses: pnpm/action-setup@v4
with:
version: 11.5.1

- run: pnpm install --frozen-lockfile

- name: "Gate: Manifest Validation"
run: npx tsx gates/validate-manifest.ts plugins/${{ matrix.plugin }}
run: pnpm exec tsx gates/validate-manifest.ts plugins/${{ matrix.plugin }}

- name: "Gate: CID Surface"
run: npx tsx gates/validate-cid-surface.ts plugins/${{ matrix.plugin }}
run: pnpm exec tsx gates/validate-cid-surface.ts plugins/${{ matrix.plugin }}

- name: "Gate: Dependencies"
run: npx tsx gates/validate-dependencies.ts plugins/${{ matrix.plugin }}
run: pnpm exec tsx gates/validate-dependencies.ts plugins/${{ matrix.plugin }}

- name: "Gate: Security Scan"
run: npx tsx gates/security-scan.ts plugins/${{ matrix.plugin }}
run: pnpm exec tsx gates/security-scan.ts plugins/${{ matrix.plugin }}

- name: "Gate: Size Audit"
run: npx tsx gates/size-audit.ts plugins/${{ matrix.plugin }}
run: pnpm exec tsx gates/size-audit.ts plugins/${{ matrix.plugin }}

- name: "Gate: Type Check"
run: |
if [ -f plugins/${{ matrix.plugin }}/src/index.ts ]; then
npx tsc --noEmit --strict --skipLibCheck \
pnpm exec tsc --noEmit --strict --skipLibCheck \
--moduleResolution bundler --module ES2022 --target ES2022 \
plugins/${{ matrix.plugin }}/src/**/*.ts || true
else
Expand All @@ -76,7 +80,7 @@ jobs:
run: |
if [ -d plugins/${{ matrix.plugin }}/tests ]; then
echo "Running plugin tests..."
npx vitest run plugins/${{ matrix.plugin }}/tests/ --passWithNoTests || echo "⚠️ Tests failed (non-blocking)"
pnpm dlx vitest run plugins/${{ matrix.plugin }}/tests/ --passWithNoTests || echo "⚠️ Tests failed (non-blocking)"
else
echo "No tests directory — skipping (non-blocking)"
fi
Expand All @@ -103,6 +107,9 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- run: npm ci || npm install --no-save
- uses: pnpm/action-setup@v4
with:
version: 11.5.1
- run: pnpm install --frozen-lockfile
- name: Verify registry builds cleanly
run: npx tsx scripts/build-registry.ts
run: pnpm exec tsx scripts/build-registry.ts
6 changes: 4 additions & 2 deletions gates/test-native-extension-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ try {
const contractsDirectory = join(fixtureDirectory, 'registry', 'contracts');
mkdirSync(contractsDirectory, { recursive: true });
const contractPath = join(contractsDirectory, 'radix-host-effects-1.0.0.json');
const contractSource = '{"contract_id":"radix-host-effects","version":"1.0.0","processes":[]}\n';
const contractSource = '{"contract_id":"radix-host-effects","version":"1.0.0","processes":[]}\r\n';
writeFileSync(contractPath, contractSource);
const contractDigest = createHash('sha256').update(contractSource).digest('hex');
const contractDigest = createHash('sha256')
.update(contractSource.replace(/\r\n/g, '\n'))
.digest('hex');
const contractsIndexPath = join(contractsDirectory, 'index.json');
writeFileSync(contractsIndexPath, JSON.stringify({
version: 1,
Expand Down
6 changes: 5 additions & 1 deletion gates/validate-host-effects-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,16 @@ if (index.version !== 1 || !Array.isArray(index.contracts)) {
throw new Error(`${indexPath} must declare version 1 and a contracts array`);
}

function canonicalContractBytes(source: Buffer): Buffer {
return Buffer.from(source.toString('utf8').replace(/\r\n/g, '\n'), 'utf8');
}

for (const published of index.contracts) {
if (!existsSync(published.path)) {
throw new Error(`published contract missing: ${published.path}`);
}
const source = readFileSync(published.path);
const digest = createHash('sha256').update(source).digest('hex');
const digest = createHash('sha256').update(canonicalContractBytes(source)).digest('hex');
if (digest !== published.sha256) {
throw new Error(`${published.id}@${published.version} digest does not match index`);
}
Expand Down
6 changes: 5 additions & 1 deletion gates/validate-native-extension-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,17 @@ function assertExactKeys(value: Record<string, unknown>, expected: readonly stri
}
}

function canonicalContractBytes(source: Buffer): Buffer {
return Buffer.from(source.toString('utf8').replace(/\r\n/g, '\n'), 'utf8');
}

function validatePublishedContract(contract: PublishedContract, contractsIndexDirectory: string): void {
if (!contract.path.startsWith('registry/contracts/') || contract.path.includes('..') || contract.path.includes('\\') || !contract.path.endsWith('.json')) {
throw new Error(`host-effects contract path must stay within registry/contracts: ${contract.path}`);
}
const contractPath = resolve(contractsIndexDirectory, '..', '..', contract.path);
const source = readFileSync(contractPath);
const digest = createHash('sha256').update(source).digest('hex');
const digest = createHash('sha256').update(canonicalContractBytes(source)).digest('hex');
if (digest !== contract.sha256) {
throw new Error(`host-effects contract digest does not match its publication index: ${contract.id}@${contract.version}`);
}
Expand Down
2 changes: 1 addition & 1 deletion registry/contracts/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"version": "1.0.0",
"radix": ">=1.55.60",
"path": "registry/contracts/radix-host-effects/1.0.0.json",
"sha256": "25888940c94374a38bc9035cafe94ee0c58ef000c9b843121576d4bd1ba02b26"
"sha256": "995f082b72af46870a977e4474ccc43555c8c55c468415a6150c3db13f59c8b9"
}
]
}