Skip to content
Draft
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
103 changes: 102 additions & 1 deletion src/extensions/artifacts/native/tools/extension-artifact-packager.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,75 @@ export function selectCatalogExtensions(extensions, qualificationSqlNames = [])
return selected;
}

export function selectMobileStaticModuleSqlNames(rows, sqlNames) {
if (!Array.isArray(rows)) {
fail('mobile static catalog rows must be an array');
}
const selected = new Set(sortedDeduped(sqlNames));
return sortedDeduped(
rows
.filter((row) => {
if (row === null || Array.isArray(row) || typeof row !== 'object') {
fail('mobile static catalog rows must be objects');
}
return selected.has(row.sqlName)
&& row.mobilePrebuilt === true
&& row.mobileStaticRequired === true
&& typeof row.stem === 'string'
&& row.stem.length > 0;
})
.map((row) => row.sqlName),
);
}

function mobileStaticSpecTable(text, label) {
const lines = text.replace(/\r\n/g, '\n').replace(/\n+$/u, '').split('\n');
if (lines.length < 2 || !lines[0].startsWith('# @generated by ')) {
fail(`${label} mobile static registry is missing its generated marker`);
}
const header = lines[1];
const columns = header.split('\t');
if (columns[0] !== 'sql-name' || columns.length !== 16) {
fail(`${label} mobile static registry has an invalid header`);
}
const rows = new Map();
for (const [offset, line] of lines.slice(2).entries()) {
if (line.length === 0) continue;
const fields = line.split('\t');
if (
fields.length < 4
|| fields.length > columns.length
|| fields.slice(0, 4).some((field) => field.length === 0)
) {
fail(`${label} mobile static registry row ${offset + 3} is invalid`);
}
if (rows.has(fields[0])) {
fail(`${label} mobile static registry repeats SQL name '${fields[0]}'`);
}
rows.set(fields[0], line);
}
return { generatedMarker: lines[0], header, rows };
}

export function mergeMobileStaticSpecRegistries(publicText, qualificationText) {
const publicTable = mobileStaticSpecTable(publicText, 'public');
const qualificationTable = mobileStaticSpecTable(qualificationText, 'qualification');
if (publicTable.header !== qualificationTable.header) {
fail('public and qualification mobile static registry headers disagree');
}
const rows = new Map(publicTable.rows);
for (const [sqlName, row] of qualificationTable.rows) {
if (rows.has(sqlName)) {
fail(`qualification mobile static registry overlaps public SQL name '${sqlName}'`);
}
rows.set(sqlName, row);
}
const body = [...rows]
.sort(([left], [right]) => compareText(left, right))
.map(([, row]) => row);
return [publicTable.generatedMarker, publicTable.header, ...body, ''].join('\n');
}

export async function catalogRows({
qualificationSqlNames = [],
qualificationTarget = undefined,
Expand Down Expand Up @@ -432,6 +501,32 @@ async function listCatalog(args = []) {
}
}

async function mobileStaticModuleSqlNames(args = []) {
if (args.length !== 4 || args[0] !== '--target' || args[2] !== '--sql-names') {
fail('mobile-static-module-sql-names requires --target <native-target> --sql-names <csv>');
}
const target = args[1];
if (!MOBILE_NATIVE_TARGETS.has(target)) {
fail(`mobile-static-module-sql-names requires a mobile native target, got '${target}'`);
}
const qualificationSqlNames = qualificationCandidateSqlNamesForTarget(target, { family: 'native' });
const rows = await catalogRows({ qualificationSqlNames, qualificationTarget: target });
console.log(selectMobileStaticModuleSqlNames(rows, splitCsv(args[3])).join(','));
}

async function mergeMobileStaticSpecs(args = []) {
if (args.length !== 3) {
fail('merge-mobile-static-specs requires <public-tsv> <qualification-tsv> <output-tsv>');
}
const [publicPath, qualificationPath, outputPath] = args;
const merged = mergeMobileStaticSpecRegistries(
await readText(publicPath),
await readText(qualificationPath),
);
await fs.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
await fs.writeFile(path.resolve(outputPath), merged);
}

async function releasePackageByProduct(product) {
const releaseConfig = await readJson(RELEASE_CONFIG_PATH);
const packages = releaseConfig.packages ?? {};
Expand Down Expand Up @@ -1279,14 +1374,20 @@ async function main() {
case 'selected-sql-names':
await selectedSqlNames(args[0] ?? '');
break;
case 'mobile-static-module-sql-names':
await mobileStaticModuleSqlNames(args);
break;
case 'merge-mobile-static-specs':
await mergeMobileStaticSpecs(args);
break;
case 'product-version':
await productVersion(args[0] ?? '');
break;
case 'create-artifact':
await createArtifact(args);
break;
default:
fail('usage: extension-artifact-packager.mjs <list-catalog|selected-sql-names|product-version|create-artifact> [options]');
fail('usage: extension-artifact-packager.mjs <list-catalog|selected-sql-names|mobile-static-module-sql-names|merge-mobile-static-specs|product-version|create-artifact> [options]');
}
}

Expand Down
74 changes: 35 additions & 39 deletions src/extensions/artifacts/native/tools/package-release-assets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,6 @@ build_sql_names="$({
printf '%s\n' "$selected_sql_names" | tr ',' '\n'
printf '%s\n' "$qualification_sql_names" | tr ',' '\n'
} | sed '/^$/d' | LC_ALL=C sort -u | paste -sd ',' -)"
if [ -n "$qualification_sql_names" ]; then
export OLIPHAUNT_MOBILE_STATIC_SPECS_TSV="$root/src/extensions/generated/mobile/qualification-static-extensions.tsv"
fi

version="${OLIPHAUNT_EXTENSION_RELEASE_VERSION:-$(bun "$packager" product-version liboliphaunt-native)}"
native_runtime_version="$(tr -d '[:space:]' < "$root/src/runtimes/liboliphaunt/native/VERSION")"
if [ "$qualification_only" = "1" ]; then
Expand Down Expand Up @@ -136,44 +132,37 @@ fi
rm -rf "$stage_root"
mkdir -p "$out_dir" "$stage_root"

csv_join() {
paste -sd ',' -
}

catalog_rows() {
awk -F '\t' 'NR > 1 { print }' "$catalog_file"
}

mobile_module_extensions_csv() {
if [ "$qualification_only" = "1" ]; then
local extension
IFS=',' read -r -a qualification_extensions <<<"$selected_sql_names"
for extension in "${qualification_extensions[@]}"; do
[ -n "$extension" ] || continue
oliphaunt_mobile_static_extension_spec "$extension" >/dev/null ||
fail "deferred candidate $extension has no generated mobile qualification spec"
done
printf '%s\n' "$selected_sql_names"
return 0
fi
{
catalog_rows | awk -F '\t' -v selected="$selected_sql_names" '
function selected_match(sql_name, selected, parts, count, i) {
if (selected == "") {
return 1
}
count = split(selected, parts, ",")
for (i = 1; i <= count; i++) {
if (parts[i] == sql_name) {
return 1
}
}
return 0
}
$8 == "yes" && $9 == "yes" && $4 != "-" && selected_match($1, selected) { print $1 }
'
printf '%s\n' "$qualification_sql_names" | tr ',' '\n' | sed '/^$/d'
} | LC_ALL=C sort -u | csv_join
local extension module_extensions
local -a selected_extensions
module_extensions="$(bun "$packager" mobile-static-module-sql-names \
--target "$target_id" \
--sql-names "$build_sql_names")"
IFS=',' read -r -a selected_extensions <<<"$module_extensions"
for extension in "${selected_extensions[@]}"; do
[ -n "$extension" ] || continue
oliphaunt_mobile_static_extension_spec "$extension" >/dev/null ||
fail "selected mobile extension $extension has no generated static build spec"
done
printf '%s\n' "$module_extensions"
}

prepare_mobile_static_specs() {
[ "$qualification_only" = "0" ] || return 0
[ -n "$qualification_sql_names" ] || return 0
local public_specs qualification_specs merged_specs
public_specs="$root/src/extensions/generated/mobile/static-extensions.tsv"
qualification_specs="$root/src/extensions/generated/mobile/qualification-static-extensions.tsv"
merged_specs="$stage_root/mobile-static-extensions.tsv"
bun "$packager" merge-mobile-static-specs \
"$public_specs" \
"$qualification_specs" \
"$merged_specs"
export OLIPHAUNT_MOBILE_STATIC_SPECS_TSV="$merged_specs"
}

selected_sql_name_matches() {
Expand Down Expand Up @@ -664,6 +653,10 @@ package_ios_target() {
require_dir "$source_runtime" "mobile host extension runtime"
runtime="$(prepare_extension_release_runtime "$source_runtime")"
tools/dev/bun.sh tools/release/platform-binary-contract.mjs --target macos-arm64 --root "$runtime"
if [ "$qualification_only" = "1" ] && [ -z "$mobile_extensions" ]; then
echo "qualified deferred native extension candidate(s) $qualification_sql_names for $target_id"
return 0
fi
ios_sim_root="$mobile_extension_work_root/$target_id/ios-simulator"
ios_device_root="$mobile_extension_work_root/$target_id/ios-device"
macos_archive_root="$mobile_extension_work_root/$target_id/macos-extension-archives"
Expand All @@ -679,7 +672,6 @@ package_ios_target() {
if [ "$qualification_only" = "1" ]; then
return 0
fi

local sql_name pg_major creates_extension stem dependencies shared_preload desktop_prebuilt mobile_prebuilt mobile_static_required mobile_static_targets data_files artifact_policy runtime_artifact ios_artifact static_prefix registration_artifact dependency dependency_xcframework dependency_artifact
while IFS=$'\t' read -r sql_name pg_major creates_extension stem dependencies shared_preload desktop_prebuilt mobile_prebuilt mobile_static_required mobile_static_targets data_files artifact_policy; do
[ -n "$sql_name" ] || continue
Expand Down Expand Up @@ -775,6 +767,10 @@ package_android_target() {
require_dir "$source_runtime" "mobile host extension runtime"
runtime="$(prepare_extension_release_runtime "$source_runtime")"
tools/dev/bun.sh tools/release/platform-binary-contract.mjs --target linux-x64-gnu --root "$runtime"
if [ "$qualification_only" = "1" ] && [ -z "$mobile_extensions" ]; then
echo "qualified deferred native extension candidate(s) $qualification_sql_names for $target_id"
return 0
fi
case "$target_id" in
android-arm64-v8a)
android_root="$mobile_extension_work_root/$target_id/android-arm64"
Expand All @@ -793,7 +789,6 @@ package_android_target() {
if [ "$qualification_only" = "1" ]; then
return 0
fi

local sql_name pg_major creates_extension stem dependencies shared_preload desktop_prebuilt mobile_prebuilt mobile_static_required mobile_static_targets data_files artifact_policy runtime_artifact android_archive static_prefix
while IFS=$'\t' read -r sql_name pg_major creates_extension stem dependencies shared_preload desktop_prebuilt mobile_prebuilt mobile_static_required mobile_static_targets data_files artifact_policy; do
[ -n "$sql_name" ] || continue
Expand Down Expand Up @@ -829,6 +824,7 @@ package_android_target() {
fetch_extension_source_assets
echo "==> Reading exact extension catalog"
bun "$packager" list-catalog --qualification-target "$target_id" >"$catalog_file"
prepare_mobile_static_specs
if [ "$qualification_only" = "0" ]; then
write_indexes
fi
Expand Down
6 changes: 6 additions & 0 deletions src/extensions/catalog/extensions.promoted.toml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ stable = true
id = "pgcrypto"
stable = true

[[extensions]]
id = "pgmq"
build = true
stable = false
blocker = "PGMQ 1.12.0 is pinned for native desktop, mobile, and WASIX qualification, but exact-commit package, lifecycle, restart, and dump/restore evidence has not yet passed; keep it out of public catalogs until that CI evidence exists."

[[extensions]]
id = "pgtap"
stable = true
Expand Down
7 changes: 7 additions & 0 deletions src/extensions/catalog/extensions.smoke.toml
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@ server = "passed"
restart = "passed"
dump-restore = "passed"

[[extensions]]
id = "pgmq"
direct = "not-run"
server = "not-run"
restart = "not-run"
dump-restore = "not-run"

[[extensions]]
id = "pgtap"
direct = "passed"
Expand Down
38 changes: 38 additions & 0 deletions src/extensions/catalog/extensions.source.json
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,44 @@
"native-module-file": "pgcrypto.so",
"notes": []
},
{
"id": "pgmq",
"sql-name": "pgmq",
"rust-constant": "PGMQ",
"display-name": "PGMQ",
"source-kind": "oliphaunt-other-extension",
"upstream-import-name": "pgmq",
"upstream-import-path": "pgmq-extension",
"package-export": "./pgmq",
"tags": [
"postgres extension",
"qualification candidate"
],
"control-file": "target/oliphaunt-sources/checkouts/pgmq/pgmq-extension/pgmq.control",
"control": {
"module-pathname": "$libdir/pgmq",
"requires": [],
"relocatable": "false",
"schema": "pgmq"
},
"dependencies": [],
"native-dependencies": [],
"load-order": [],
"lifecycle": {
"create-extension": true,
"create-schema": "pgmq",
"load-sql": [],
"post-create-sql": [],
"startup-config": [],
"preload-required": false,
"restart-required": false,
"shared-memory-required": false
},
"tests": [
"src/extensions/external/pgmq/tests/smoke.sql"
],
"notes": []
},
{
"id": "pgtap",
"sql-name": "pgtap",
Expand Down
6 changes: 3 additions & 3 deletions src/extensions/contrib/.release-semantic-inputs.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@
"inputs": [
{
"path": "src/extensions/artifacts/native/tools/extension-artifact-packager.mjs",
"sha256": "14abaf5b3828900fdc3c561aa7298c3e026623f14f71292b708c3cf3edaca3ae"
"sha256": "46dd355991a820ce8cd4cb9e0649a56f1d8d0d0ec7158b66686e4ca9706a09af"
},
{
"path": "src/extensions/artifacts/native/tools/package-release-assets.sh",
"sha256": "3bba3ae4486549321911a68c2e6e6ecd2d1c22640edc8253ec5079aaeb3fe657"
"sha256": "cff8891218fbfcf6d71665b28840d8da6b78cec7b114db312a2aa3d636e5d919"
},
{
"path": "src/extensions/artifacts/native/tools/stage-windows-binary-contract.mjs",
Expand Down Expand Up @@ -377,5 +377,5 @@
]
}
],
"sha256": "419f620e12df219054374188e11b362780ba9e436f547348c20acd0bf7c29d24"
"sha256": "7c5e0abdb40e3fd08962f78f3345018d55fbfc0433df30eceaef8cf63dd01931"
}
9 changes: 9 additions & 0 deletions src/extensions/evidence/matrix.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ source-digest-inputs = [
"src/extensions/external/pg_ivm/source.toml",
"src/extensions/external/pg_textsearch/source.toml",
"src/extensions/external/pg_uuidv7/source.toml",
"src/extensions/external/pgmq/source.toml",
"src/extensions/external/pgtap/source.toml",
"src/extensions/external/postgis/dependencies/geos/source.toml",
"src/extensions/external/postgis/dependencies/json-c/source.toml",
Expand All @@ -41,6 +42,14 @@ source-digest-inputs = [
"src/extensions/external/pg_textsearch/upstream-license-data.json",
"src/extensions/external/pg_uuidv7/moon.yml",
"src/extensions/external/pg_uuidv7/upstream-license-data.json",
"src/extensions/external/pgmq/moon.yml",
"src/extensions/external/pgmq/recipe.toml",
"src/extensions/external/pgmq/targets/mobile.toml",
"src/extensions/external/pgmq/targets/native.toml",
"src/extensions/external/pgmq/targets/wasix.toml",
"src/extensions/external/pgmq/tests/smoke.sql",
"src/extensions/external/pgmq/tests/upstream.toml",
"src/extensions/external/pgmq/upstream-license-data.json",
"src/extensions/external/pgtap/moon.yml",
"src/extensions/external/pgtap/recipe.toml",
"src/extensions/external/pgtap/targets/native-static-registry.toml",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@
"inputs": [
{
"path": "src/extensions/artifacts/native/tools/extension-artifact-packager.mjs",
"sha256": "14abaf5b3828900fdc3c561aa7298c3e026623f14f71292b708c3cf3edaca3ae"
"sha256": "46dd355991a820ce8cd4cb9e0649a56f1d8d0d0ec7158b66686e4ca9706a09af"
},
{
"path": "src/extensions/artifacts/native/tools/package-release-assets.sh",
"sha256": "3bba3ae4486549321911a68c2e6e6ecd2d1c22640edc8253ec5079aaeb3fe657"
"sha256": "cff8891218fbfcf6d71665b28840d8da6b78cec7b114db312a2aa3d636e5d919"
},
{
"path": "src/extensions/artifacts/native/tools/stage-windows-binary-contract.mjs",
Expand Down Expand Up @@ -343,5 +343,5 @@
]
}
],
"sha256": "cd9da3b78ef5e8df01dacab7774205aef15270bf3d20dee1718b81bad78c13b1"
"sha256": "ee22c0b1c0f38105e73a248c2ebc61ec73350a92330dfaa911fb058820c9c773"
}
Loading