feat: prepare CASAN paid PoC release package
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env node
|
||||
// Generate a reproducible third-party inventory from the committed npm lockfile.
|
||||
// This is an inventory, not legal advice; every release must regenerate and review it.
|
||||
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
|
||||
const [output = 'docs/commercial/THIRD_PARTY_SOFTWARE.json'] = process.argv.slice(2);
|
||||
const lock = JSON.parse(readFileSync('package-lock.json', 'utf8'));
|
||||
const exceptionsPath = 'docs/commercial/LICENSE_EXCEPTIONS.json';
|
||||
const exceptions = existsSync(exceptionsPath) ? JSON.parse(readFileSync(exceptionsPath, 'utf8')).exceptions ?? {} : {};
|
||||
const workspaceLicenses = new Map(Object.entries(lock.packages ?? {})
|
||||
.filter(([path, value]) => path && !path.startsWith('node_modules/') && value && typeof value === 'object' && typeof value.name === 'string')
|
||||
.map(([, value]) => [value.name, typeof value.license === 'string' ? value.license : 'UNKNOWN']));
|
||||
const rows = Object.entries(lock.packages ?? {})
|
||||
.filter(([path, value]) => path.startsWith('node_modules/') && value && typeof value === 'object')
|
||||
.map(([path, value]) => {
|
||||
const name = path.slice('node_modules/'.length);
|
||||
const key = `${name}@${String(value.version ?? 'unknown')}`;
|
||||
const declared = typeof value.license === 'string' ? value.license : (workspaceLicenses.get(name) ?? 'UNKNOWN');
|
||||
const exception = exceptions[key];
|
||||
return {
|
||||
name,
|
||||
version: String(value.version ?? 'unknown'),
|
||||
declared_license: declared,
|
||||
license: typeof exception?.reviewed_license === 'string' ? exception.reviewed_license : declared,
|
||||
review_evidence: typeof exception?.evidence === 'string' ? exception.evidence : null,
|
||||
resolved: typeof value.resolved === 'string' ? value.resolved : null,
|
||||
integrity: typeof value.integrity === 'string' ? value.integrity : null,
|
||||
};
|
||||
})
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
const licenses = {};
|
||||
for (const row of rows) licenses[row.license] = (licenses[row.license] ?? 0) + 1;
|
||||
const inventory = {
|
||||
schema_version: '1.0',
|
||||
generated_from: 'package-lock.json',
|
||||
generated_at: new Date().toISOString(),
|
||||
scope: 'npm dependencies recorded in the root lockfile; Python, container base images, model and dataset licenses require separate release review.',
|
||||
exceptions_source: existsSync(exceptionsPath) ? exceptionsPath : null,
|
||||
package_count: rows.length,
|
||||
licenses,
|
||||
packages: rows,
|
||||
};
|
||||
const destination = resolve(output);
|
||||
mkdirSync(dirname(destination), { recursive: true });
|
||||
writeFileSync(destination, `${JSON.stringify(inventory, null, 2)}\n`);
|
||||
console.log(`LICENSE_INVENTORY_CREATED path=${output} packages=${rows.length} licenses=${Object.keys(licenses).length}`);
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Smoke the release exactly as a customer receives it. This intentionally
|
||||
# extracts into a fresh directory and never resolves files from the source tree.
|
||||
#
|
||||
# Usage: scripts/smoke-release-artifact.sh dist/casan-<bundle>-vX.Y.Z.tar.gz
|
||||
# Env: CASAN_ARTIFACT_SMOKE_RUN_TESTS=1 # platform: npm ci + tests + build
|
||||
|
||||
ARCHIVE="${1:-}"
|
||||
[[ -n "$ARCHIVE" && -f "$ARCHIVE" ]] || {
|
||||
echo "ARTIFACT_SMOKE_FAIL reason=archive_missing path=${ARCHIVE:-unset}" >&2
|
||||
exit 64
|
||||
}
|
||||
ARCHIVE="$(cd "$(dirname "$ARCHIVE")" && pwd)/$(basename "$ARCHIVE")"
|
||||
|
||||
WORK="$(mktemp -d)"
|
||||
cleanup() { rm -rf "$WORK"; }
|
||||
trap cleanup EXIT
|
||||
|
||||
tar -xzf "$ARCHIVE" -C "$WORK"
|
||||
roots=()
|
||||
while IFS= read -r directory; do
|
||||
roots+=("$directory")
|
||||
done < <(find "$WORK" -mindepth 1 -maxdepth 1 -type d -print)
|
||||
[[ "${#roots[@]}" -eq 1 ]] || {
|
||||
echo "ARTIFACT_SMOKE_FAIL reason=unexpected_root_count count=${#roots[@]}" >&2
|
||||
exit 1
|
||||
}
|
||||
ROOT="${roots[0]}"
|
||||
|
||||
required=(BUNDLE-MANIFEST.txt SHA256SUMS VERSION bin/casan packages/casan-harness)
|
||||
for path in "${required[@]}"; do
|
||||
[[ -e "$ROOT/$path" ]] || {
|
||||
echo "ARTIFACT_SMOKE_FAIL reason=required_path_missing path=$path" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
(cd "$ROOT" && shasum -a 256 -c SHA256SUMS >/dev/null)
|
||||
VERSION="$(tr -d '[:space:]' < "$ROOT/VERSION")"
|
||||
CLI_VERSION="$(cd "$ROOT" && ./bin/casan version)"
|
||||
[[ "$CLI_VERSION" == "casan $VERSION" ]] || {
|
||||
echo "ARTIFACT_SMOKE_FAIL reason=cli_version_mismatch expected=$VERSION actual=$CLI_VERSION" >&2
|
||||
exit 1
|
||||
}
|
||||
(cd "$ROOT" && ./bin/casan help >/dev/null)
|
||||
|
||||
STATUS="$(awk '/^status:/ {print $2}' "$ROOT/BUNDLE-MANIFEST.txt")"
|
||||
BUNDLE="$(awk '/^bundle:/ {print $2}' "$ROOT/BUNDLE-MANIFEST.txt")"
|
||||
|
||||
if [[ "$BUNDLE" == "platform" ]]; then
|
||||
platform_required=(
|
||||
packages/casan-control-panel/backend/package.json
|
||||
packages/casan-control-panel/frontend/package.json
|
||||
Dockerfile.control-panel-api
|
||||
Dockerfile.control-panel-ui
|
||||
docker-compose.control-panel.yml
|
||||
docker-compose.control-panel.local.yml
|
||||
nginx/control-panel.conf
|
||||
infra/production/README.md
|
||||
docs/security/CONTROL_PANEL_AUTH_HARDENING.md
|
||||
)
|
||||
for path in "${platform_required[@]}"; do
|
||||
[[ -e "$ROOT/$path" ]] || {
|
||||
echo "ARTIFACT_SMOKE_FAIL reason=platform_path_missing path=$path" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
[[ -f "$ROOT/PREVIEW-INCOMPLETE.txt" ]] || {
|
||||
echo "ARTIFACT_SMOKE_FAIL reason=preview_marker_missing" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ "${CASAN_ARTIFACT_SMOKE_RUN_TESTS:-0}" == "1" ]]; then
|
||||
command -v npm >/dev/null 2>&1 || {
|
||||
echo "ARTIFACT_SMOKE_FAIL reason=npm_missing" >&2
|
||||
exit 1
|
||||
}
|
||||
(cd "$ROOT" && npm ci && npm run console:test && npm run console:build)
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "ARTIFACT_SMOKE_PASS bundle=$BUNDLE status=$STATUS version=$VERSION extracted=true checksums=true"
|
||||
Reference in New Issue
Block a user