From a52c517b95d93a549397dec3da15eda2287c200f Mon Sep 17 00:00:00 2001 From: Natoandro Date: Mon, 16 Dec 2024 12:25:46 +0300 Subject: [PATCH] wip --- .ghjk/deno.lock | 11 + .ghjk/lock.json | 2 +- deno.lock | 3 + ghjk.ts | 24 +- src/common/src/typegraph/mod.rs | 3 + src/typegate/src/engine/planner/mod.ts | 9 +- src/typegate/src/runtimes/deno/deno.ts | 1 + .../src/transports/graphql/typegraph.ts | 23 + src/typegate/src/typegate/mod.ts | 21 +- src/typegate/src/typegate/register.ts | 8 + src/typegate/src/typegraph/types.ts | 2 +- src/typegate/src/typegraph/versions.ts | 2 +- src/typegraph/core/src/typegraph.rs | 2 +- tests/e2e/published/config.ts | 61 +++ tests/e2e/published/published_test.ts | 455 ------------------ tests/e2e/published/sdk_test.ts | 177 +++++++ tests/e2e/published/typegate_upgrade_test.ts | 194 ++++++++ tests/e2e/published/utils.ts | 76 +++ tools/consts.ts | 27 +- tools/deps.ts | 1 + tools/jsr/deno2node.ts | 4 +- tools/jsr/jsr-gen.ts | 4 +- tools/tasks/lock.ts | 38 +- 23 files changed, 650 insertions(+), 498 deletions(-) create mode 100644 tests/e2e/published/config.ts delete mode 100644 tests/e2e/published/published_test.ts create mode 100644 tests/e2e/published/sdk_test.ts create mode 100644 tests/e2e/published/typegate_upgrade_test.ts create mode 100644 tests/e2e/published/utils.ts diff --git a/.ghjk/deno.lock b/.ghjk/deno.lock index 0223fbf50..8674050dd 100644 --- a/.ghjk/deno.lock +++ b/.ghjk/deno.lock @@ -5,6 +5,7 @@ "jsr:@david/dax@0.41.0": "jsr:@david/dax@0.41.0", "jsr:@david/which@^0.4.1": "jsr:@david/which@0.4.1", "jsr:@std/assert@^0.221.0": "jsr:@std/assert@0.221.0", + "jsr:@std/assert@^1.0.3": "jsr:@std/assert@1.0.6", "jsr:@std/bytes@^0.221.0": "jsr:@std/bytes@0.221.0", "jsr:@std/bytes@^1.0.2": "jsr:@std/bytes@1.0.2", "jsr:@std/cli@^1.0.3": "jsr:@std/cli@1.0.5", @@ -14,6 +15,7 @@ "jsr:@std/fs": "jsr:@std/fs@0.221.0", "jsr:@std/fs@0.221.0": "jsr:@std/fs@0.221.0", "jsr:@std/fs@^1.0.1": "jsr:@std/fs@1.0.3", + "jsr:@std/internal@^1.0.4": "jsr:@std/internal@1.0.4", "jsr:@std/io@0.221.0": "jsr:@std/io@0.221.0", "jsr:@std/io@^0.221.0": "jsr:@std/io@0.221.0", "jsr:@std/path": "jsr:@std/path@0.221.0", @@ -49,6 +51,12 @@ "@std/assert@0.221.0": { "integrity": "a5f1aa6e7909dbea271754fd4ab3f4e687aeff4873b4cef9a320af813adb489a" }, + "@std/assert@1.0.6": { + "integrity": "1904c05806a25d94fe791d6d883b685c9e2dcd60e4f9fc30f4fc5cf010c72207", + "dependencies": [ + "jsr:@std/internal@^1.0.4" + ] + }, "@std/bytes@0.221.0": { "integrity": "64a047011cf833890a4a2ab7293ac55a1b4f5a050624ebc6a0159c357de91966" }, @@ -77,6 +85,9 @@ "jsr:@std/path@^1.0.4" ] }, + "@std/internal@1.0.4": { + "integrity": "62e8e4911527e5e4f307741a795c0b0a9e6958d0b3790716ae71ce085f755422" + }, "@std/io@0.221.0": { "integrity": "faf7f8700d46ab527fa05cc6167f4b97701a06c413024431c6b4d207caa010da", "dependencies": [ diff --git a/.ghjk/lock.json b/.ghjk/lock.json index a2e92294b..02bd5a89b 100644 --- a/.ghjk/lock.json +++ b/.ghjk/lock.json @@ -988,7 +988,7 @@ "version-print": { "ty": "denoFile@v1", "key": "version-print", - "desc": "Print $METATYPE_VERSION", + "desc": "Print $CURRENT_VERSION", "envKey": "bciqori26ml2iqph3izifvvfsf4b2ar3yddr344utbfstyk2v33ot3mq" }, "test-rust": { diff --git a/deno.lock b/deno.lock index 12669bd8b..6458ce5a9 100644 --- a/deno.lock +++ b/deno.lock @@ -5,6 +5,8 @@ "jsr:@david/dax@0.41.0": "jsr:@david/dax@0.41.0", "jsr:@david/which@^0.4.1": "jsr:@david/which@0.4.1", "jsr:@std/assert@^0.221.0": "jsr:@std/assert@0.221.0", + "jsr:@std/assert@^1.0.2": "jsr:@std/assert@1.0.6", + "jsr:@std/assert@^1.0.3": "jsr:@std/assert@1.0.6", "jsr:@std/assert@^1.0.4": "jsr:@std/assert@1.0.6", "jsr:@std/assert@^1.0.6": "jsr:@std/assert@1.0.6", "jsr:@std/async@^1.0.3": "jsr:@std/async@1.0.5", @@ -40,6 +42,7 @@ "jsr:@std/path@^1.0.2": "jsr:@std/path@1.0.8", "jsr:@std/path@^1.0.4": "jsr:@std/path@1.0.4", "jsr:@std/path@^1.0.8": "jsr:@std/path@1.0.8", + "jsr:@std/semver": "jsr:@std/semver@1.0.3", "jsr:@std/semver@^1.0.1": "jsr:@std/semver@1.0.3", "jsr:@std/streams@0.221.0": "jsr:@std/streams@0.221.0", "jsr:@std/streams@1": "jsr:@std/streams@1.0.4", diff --git a/ghjk.ts b/ghjk.ts index b11abd6ae..2fa4e33b9 100644 --- a/ghjk.ts +++ b/ghjk.ts @@ -3,8 +3,13 @@ // @ts-nocheck: Deno file -import { METATYPE_VERSION, PUBLISHED_VERSION } from "./tools/consts.ts"; +import { + CURRENT_VERSION, + LATEST_PRE_RELEASE_VERSION, + LATEST_RELEASE_VERSION, +} from "./tools/consts.ts"; import { file, ports, sedLock, semver, stdDeps } from "./tools/deps.ts"; +import { validateVersions } from "./tools/tasks/lock.ts"; import installs from "./tools/installs.ts"; import tasks from "./tools/tasks/mod.ts"; @@ -116,11 +121,12 @@ env("dev") ports.cargobi({ crateName: "git-cliff", locked: true }), ); -task("version-print", () => console.log(METATYPE_VERSION), { - desc: "Print $METATYPE_VERSION", +task("version-print", () => console.log(CURRENT_VERSION), { + desc: "Print $CURRENT_VERSION", }); task("version-bump", async ($) => { + validateVersions(); const bumps = [ "major", "premajor", @@ -140,7 +146,7 @@ task("version-bump", async ($) => { const newVersion = semver.format( semver.increment( - semver.parse(METATYPE_VERSION), + semver.parse(CURRENT_VERSION), bump as semver.ReleaseType, { prerelease: "rc", @@ -148,15 +154,17 @@ task("version-bump", async ($) => { ), ); - $.logStep(`Bumping ${METATYPE_VERSION} → ${newVersion}`); - const lines = [[/^(export const METATYPE_VERSION = ").*(";)$/, newVersion]]; + $.logStep(`Bumping ${CURRENT_VERSION} → ${newVersion}`); + const lines = [[/^(export const CURRENT_VERSION = ").*(";)$/, newVersion]]; if (bump === "prerelease") { $.logStep( - `Bumping published version ${PUBLISHED_VERSION} → ${METATYPE_VERSION}`, + `Bumping published version ${ + LATEST_PRE_RELEASE_VERSION || LATEST_RELEASE_VERSION + } → ${CURRENT_VERSION}`, ); lines.push([ /^(export const PUBLISHED_VERSION = ").*(";)$/, - METATYPE_VERSION, + CURRENT_VERSION, ]); } diff --git a/src/common/src/typegraph/mod.rs b/src/common/src/typegraph/mod.rs index f33c8857e..f43cc59f1 100644 --- a/src/common/src/typegraph/mod.rs +++ b/src/common/src/typegraph/mod.rs @@ -97,6 +97,9 @@ pub struct TypeMeta { pub version: String, pub random_seed: Option, pub artifacts: BTreeMap, + #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default)] + pub namespaces: Vec, } #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] diff --git a/src/typegate/src/engine/planner/mod.ts b/src/typegate/src/engine/planner/mod.ts index 59d77e2a6..9c42d2273 100644 --- a/src/typegate/src/engine/planner/mod.ts +++ b/src/typegate/src/engine/planner/mod.ts @@ -545,12 +545,13 @@ export class Planner { const inputType = this.tg.type(inputIdx, Type.OBJECT); const argumentTypes = mapValues( inputType.properties, - (idx, key) => - this.tg.getGraphQLType( + (idx, key) => { + return this.tg.getGraphQLType( this.tg.type(idx), false, - inputType.id.includes(key), - ), + inputType.id?.includes(key) ?? false, + ); + }, ); const stage = this.createComputeStage(node, { diff --git a/src/typegate/src/runtimes/deno/deno.ts b/src/typegate/src/runtimes/deno/deno.ts index c98ad0f50..bfd8130fa 100644 --- a/src/typegate/src/runtimes/deno/deno.ts +++ b/src/typegate/src/runtimes/deno/deno.ts @@ -215,6 +215,7 @@ export class DenoRuntime extends Runtime { return [stage.withResolver(this.delegate(mat, verbose))]; } + console.log("root type", this.tg.types[0]); if (this.tg.meta.namespaces!.includes(stage.props.typeIdx)) { return [stage.withResolver(() => ({}))]; } diff --git a/src/typegate/src/transports/graphql/typegraph.ts b/src/typegate/src/transports/graphql/typegraph.ts index b504a6cb9..74c94eabe 100644 --- a/src/typegate/src/transports/graphql/typegraph.ts +++ b/src/typegate/src/transports/graphql/typegraph.ts @@ -144,3 +144,26 @@ export function parseGraphQLTypeGraph(tgOrig: TypeGraphDS): TypeGraphDS { return typegraph; } + +// TEMPORARY +export function setNamespaces(tg: TypeGraphDS) { + if (tg.meta.namespaces != null) { + return; + } + const namespaces: number[] = []; + + const rootNode = tg.types[0] as ObjectNode; + + const addNamespacesFrom = (node: ObjectNode, nodeIdx: number) => { + namespaces.push(nodeIdx); + for (const [, typeIdx] of Object.entries(node.properties)) { + const childNode = tg.types[typeIdx]; + if (childNode.type === Type.OBJECT) { + addNamespacesFrom(childNode, typeIdx); + } + } + }; + + addNamespacesFrom(rootNode, 0); + tg.meta.namespaces = namespaces; +} diff --git a/src/typegate/src/typegate/mod.ts b/src/typegate/src/typegate/mod.ts index 05c73ee53..143b3f4d0 100644 --- a/src/typegate/src/typegate/mod.ts +++ b/src/typegate/src/typegate/mod.ts @@ -33,7 +33,9 @@ import { handleGraphQL } from "../services/graphql_service.ts"; import { getLogger } from "../log.ts"; import { MigrationFailure } from "../runtimes/prisma/hooks/run_migrations.ts"; import { DenoFailure } from "../runtimes/deno/hooks/mod.ts"; -import introspectionJson from "../typegraphs/introspection.json" with { type: "json" }; +import introspectionJson from "../typegraphs/introspection.json" with { + type: "json", +}; import { ArtifactService } from "../services/artifact_service.ts"; import type { ArtifactStore } from "./artifacts/mod.ts"; // TODO move from tests (MET-497) @@ -360,6 +362,7 @@ export class Typegate implements AsyncDisposable { ); logger.info(`Registering engine '${name}'`); + logger.info("registering {}", this.register.constructor.name); await this.register.add(engine); const newArtifacts = new Set( @@ -402,14 +405,14 @@ export class Typegate implements AsyncDisposable { const introspection = enableIntrospection ? await TypeGraph.init( - this, - introspectionDef, - new SecretManager(introspectionDef, {}), - { - typegraph: TypeGraphRuntime.init(tgDS, [], {}), - }, - null, - ) + this, + introspectionDef, + new SecretManager(introspectionDef, {}), + { + typegraph: TypeGraphRuntime.init(tgDS, [], {}), + }, + null, + ) : null; const tg = await TypeGraph.init( diff --git a/src/typegate/src/typegate/register.ts b/src/typegate/src/typegate/register.ts index 34d13cb26..213c604c6 100644 --- a/src/typegate/src/typegate/register.ts +++ b/src/typegate/src/typegate/register.ts @@ -11,6 +11,7 @@ import { } from "../typegraph/versions.ts"; import { typegraphIdSchema, type TypegraphStore } from "../sync/typegraph.ts"; import { RedisReplicatedMap } from "../sync/replicated_map.ts"; +import { setNamespaces } from "../transports/graphql/typegraph.ts"; export interface MessageEntry { type: "info" | "warning" | "error"; @@ -60,6 +61,12 @@ export class ReplicatedRegister extends Register { typegraphId, ); + // temparary hack + // FIXME why are namespaces not set?? + if (tg.meta.namespaces == null) { + setNamespaces(tg); + } + // typegraph is updated while being pushed, this is only for initial load const hasUpgrade = (initialLoad && isTypegraphUpToDate(tg)) || true; console.log({ hasUpgrade }); @@ -97,6 +104,7 @@ export class ReplicatedRegister extends Register { } async add(engine: QueryEngine): Promise { + console.debug("meta", engine.tg.tg.meta); if (SystemTypegraph.check(engine.name)) { // no need for a sync this.replicatedMap.memory.set(engine.name, engine); diff --git a/src/typegate/src/typegraph/types.ts b/src/typegate/src/typegraph/types.ts index ce2d855bc..2cdf64c5c 100644 --- a/src/typegate/src/typegraph/types.ts +++ b/src/typegate/src/typegraph/types.ts @@ -68,7 +68,7 @@ export type ObjectNode = { [k: string]: number; }; required?: string[]; - id: string[]; + id?: string[]; }; export type ListNode = { type: "list"; diff --git a/src/typegate/src/typegraph/versions.ts b/src/typegate/src/typegraph/versions.ts index 47ecfdd4a..a6b8b4381 100644 --- a/src/typegate/src/typegraph/versions.ts +++ b/src/typegate/src/typegraph/versions.ts @@ -56,7 +56,7 @@ const typegraphChangelog: Record< const prop = x.types[typeIdx]; if ("injection" in prop) { console.log({ injection: prop.injection, path }); - throw new Error("injection"); + // throw new Error("injection"); } if (prop.type === Type.OBJECT) { traverse(prop); diff --git a/src/typegraph/core/src/typegraph.rs b/src/typegraph/core/src/typegraph.rs index b5480ddaf..fa41dc8d2 100644 --- a/src/typegraph/core/src/typegraph.rs +++ b/src/typegraph/core/src/typegraph.rs @@ -108,7 +108,6 @@ pub fn init(params: TypegraphInitParams) -> Result<()> { dynamic: params.dynamic.unwrap_or(true), endpoints: vec![], }, - cors: params.cors.into(), auths: vec![], prefix: params.prefix, @@ -117,6 +116,7 @@ pub fn init(params: TypegraphInitParams) -> Result<()> { outjection_secrets: vec![], random_seed: Default::default(), artifacts: Default::default(), + namespaces: Default::default(), }, types: vec![], saved_store_state: Some(Store::save()), diff --git a/tests/e2e/published/config.ts b/tests/e2e/published/config.ts new file mode 100644 index 000000000..99e3b447b --- /dev/null +++ b/tests/e2e/published/config.ts @@ -0,0 +1,61 @@ +import { transformSyncConfig } from "@metatype/typegate/config.ts"; +import { clearSyncData, setupSync } from "test-utils/hooks.ts"; + +const defaultSyncEnvs = { + // SYNC_REDIS_URL: "redis://:password@localhost:6379/12", + SYNC_S3_HOST: "http://localhost:9000", + SYNC_S3_REGION: "local", + SYNC_S3_ACCESS_KEY: "minio", + SYNC_S3_SECRET_KEY: "password", + // SYNC_S3_BUCKET: "upgrade-test", + SYNC_S3_PATH_STYLE: "true", +}; + +export function config(p: { redisDb: number; s3Bucket: string }) { + const syncEnvs = { + SYNC_REDIS_URL: `redis://:password@localhost:6379/${p.redisDb}`, + SYNC_S3_BUCKET: p.s3Bucket, + ...defaultSyncEnvs, + }; + const syncConfig = transformSyncConfig({ + redis_url: new URL(syncEnvs.SYNC_REDIS_URL), + s3_host: new URL(syncEnvs.SYNC_S3_HOST), + s3_region: syncEnvs.SYNC_S3_REGION, + s3_access_key: syncEnvs.SYNC_S3_ACCESS_KEY, + s3_secret_key: syncEnvs.SYNC_S3_SECRET_KEY, + s3_bucket: syncEnvs.SYNC_S3_BUCKET, + s3_path_style: true, + }); + + return { syncConfig, syncEnvs }; +} + +export class Config { + syncEnvs: Record; + syncConfig: ReturnType; + + constructor(redisDb: number, s3Bucket: string) { + this.syncEnvs = { + SYNC_REDIS_URL: `redis://:password@localhost:6379/${redisDb}`, + SYNC_S3_BUCKET: s3Bucket, + ...defaultSyncEnvs, + }; + this.syncConfig = transformSyncConfig({ + redis_url: new URL(this.syncEnvs.SYNC_REDIS_URL), + s3_host: new URL(this.syncEnvs.SYNC_S3_HOST), + s3_region: this.syncEnvs.SYNC_S3_REGION, + s3_access_key: this.syncEnvs.SYNC_S3_ACCESS_KEY, + s3_secret_key: this.syncEnvs.SYNC_S3_SECRET_KEY, + s3_bucket: this.syncEnvs.SYNC_S3_BUCKET, + s3_path_style: true, + }); + } + + async clearSyncData() { + await clearSyncData(this.syncConfig); + } + + async setupSync() { + await setupSync(this.syncConfig); + } +} diff --git a/tests/e2e/published/published_test.ts b/tests/e2e/published/published_test.ts deleted file mode 100644 index 8eb2eb437..000000000 --- a/tests/e2e/published/published_test.ts +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. -// SPDX-License-Identifier: MPL-2.0 - -import { Meta } from "test-utils/mod.ts"; -import { projectDir } from "@local/tools/utils.ts"; -import { $ } from "@local/tools/deps.ts"; -import { PUBLISHED_VERSION, PYTHON_VERSION } from "@local/tools/consts.ts"; -import { download } from "download"; -import { Untar } from "@std/archive/untar"; -import { copy } from "@std/io/copy"; -import { readerFromStreamReader } from "@std/io/reader-from-stream-reader"; -import { encodeBase64 } from "@std/encoding/base64"; -import { Lines } from "test-utils/process.ts"; -import { newTempDir } from "test-utils/dir.ts"; -import { transformSyncConfig } from "@metatype/typegate/config.ts"; -import { clearSyncData, setupSync } from "test-utils/hooks.ts"; -import { assertEquals } from "@std/assert"; - -// const previousVersion = PUBLISHED_VERSION; -const previousVersion = "0.4.10"; - -const tempDir = $.path(projectDir).join("tmp"); - -function getAssetName(version: string) { - return `meta-cli-v${version}-${Deno.build.target}`; -} - -const syncEnvs = { - SYNC_REDIS_URL: "redis://:password@localhost:6379/12", - SYNC_S3_HOST: "http://localhost:9000", - SYNC_S3_REGION: "local", - SYNC_S3_ACCESS_KEY: "minio", - SYNC_S3_SECRET_KEY: "password", - SYNC_S3_BUCKET: "upgrade-test", - SYNC_S3_PATH_STYLE: "true", -}; - -const syncConfig = transformSyncConfig({ - redis_url: new URL(syncEnvs.SYNC_REDIS_URL), - s3_host: new URL(syncEnvs.SYNC_S3_HOST), - s3_region: syncEnvs.SYNC_S3_REGION, - s3_access_key: syncEnvs.SYNC_S3_ACCESS_KEY, - s3_secret_key: syncEnvs.SYNC_S3_SECRET_KEY, - s3_bucket: syncEnvs.SYNC_S3_BUCKET, - s3_path_style: true, -}); -console.log(syncConfig); - -// TODO remove after the next release -// The build.rs script now uses a META_CMD env var allowing us -// to use meta-old -const disabled = [ - "metagen-rs.ts", -]; - -async function checkMetaBin(path: typeof tempDir, version: string) { - try { - if (!(await path.exists())) { - return false; - } - const res = await $`bash -c 'meta-old --version'` - .env("PATH", `${path.parent()!.toString()}:${Deno.env.get("PATH")}`) - .stdout("piped"); - if (res.stdout.includes(version)) { - return true; - } - throw new Error(`version mismatch: ${res.stdout}`); - } catch (e) { - console.error(e); - return false; - } -} - -// download the fat version of the cli on the latest stable release -export async function downloadAndExtractCli(version: string) { - const name = getAssetName(version); - const extractTargetDir = tempDir.join(name); - const metaBin = extractTargetDir.join("meta-old"); - if (await checkMetaBin(metaBin, version)) { - return metaBin.toString(); - } - const url = - `https://github.com/metatypedev/metatype/releases/download/v${version}/${name}.tar.gz`; - console.log("Downloading from", url); - const archiveName = `${name}.tar.gz`; - const _fileObj = await download(url, { - file: archiveName, - dir: tempDir.toString(), - }); - const archivePath = tempDir.join(archiveName); - using file = await Deno.open(archivePath.toString()); - const reader = file.readable.pipeThrough(new DecompressionStream("gzip")); - const untar = new Untar(readerFromStreamReader(reader.getReader())); - - await extractTargetDir.ensureDir(); - - for await (const entry of untar) { - if (entry.fileName !== "meta") { - throw new Error("unexpected"); - } - using target = await Deno.open(metaBin.toString(), { - create: true, - write: true, - mode: 0o755, - }); - const res = await copy(entry, target); - console.log(`successfully written ${res} bytes`); - } - - await Deno.remove(archivePath.toString()); - - if (!(await checkMetaBin(metaBin, version))) { - throw new Error("unexpected"); - } - return metaBin.toString(); -} - -// This also tests the published NPM version of the SDK -Meta.test.only( - { - name: "typegate upgrade", - async setup() { - await clearSyncData(syncConfig); - await setupSync(syncConfig); - }, - async teardown() { - await clearSyncData(syncConfig); - }, - }, - async (t) => { - let publishedBin = ""; - await t.should("download published cli (fat version)", async () => { - publishedBin = await downloadAndExtractCli(previousVersion); - }); - - const metaBinDir = $.path(publishedBin).parent()!.toString(); - const tgSecret = encodeBase64( - globalThis.crypto.getRandomValues(new Uint8Array(64)), - ); - - const typegateTempDir = await newTempDir(); - const repoDir = await newTempDir(); - const examplesDir = $.path( - await newTempDir({ - dir: undefined, - }), - ); - t.addCleanup(async () => { - await $.co([ - $.removeIfExists(typegateTempDir), - $.removeIfExists(repoDir), - // $.removeIfExists(examplesDir), - ]); - }); - - const port = String(t.port + 1); - - const proc = new Deno.Command("meta-old", { - args: ["typegate"], - env: { - ...Deno.env.toObject(), - LOG_LEVEL: "DEBUG", - PATH: `${metaBinDir}:${Deno.env.get("PATH")}`, - TG_SECRET: tgSecret, - TG_ADMIN_PASSWORD: "password", - TMP_DIR: typegateTempDir, - TG_PORT: port, - // TODO should not be necessary - VERSION: previousVersion, - ...syncEnvs, - }, - stdout: "piped", - }).spawn(); - - await t.should( - "download example typegraphs for the published version", - async () => { - const tag = `v${previousVersion}`; - - // FIXME: cache across test runs - await $`git clone https://github.com/metatypedev/metatype.git --depth 1 --branch ${tag}` - .cwd(repoDir) - .stdout("piped") - .stderr("piped") - .printCommand(); - - await $.path(repoDir).join("metatype/examples").copy(examplesDir, { - overwrite: true, - }); - const typegraphsDir = examplesDir.join("typegraphs"); - for await (const entry of typegraphsDir.readDir()) { - const path = typegraphsDir.relative(entry.path); - if (disabled.includes(path.toString())) { - await entry.path.remove().catch((_e) => {}); - } - } - - // NOTE: we clean out the deno.json used by the examples - // before adding the published version - // by default @typegraph/sdk/ needs that trailing slash - // due to https://github.com/WICG/import-maps?tab=readme-ov-file#packages-via-trailing-slashes - await examplesDir.join("deno.json").writeJson({}); - await $.raw`bash -c 'deno add @typegraph/sdk@${previousVersion}'` - .cwd(examplesDir) - .stdout("inherit") - .printCommand(); - }, - ); - - const typegraphs: string[] = []; - - const stdout = new Lines(proc.stdout); - await stdout.readWhile((line) => { - console.log(`typegate>`, line); - return !line.includes(`typegate ready on ${port}`); - }); - stdout.readWhile((line) => { - const match = line.match(/Initializing engine '(.+)'/); - if (match) { - typegraphs.push(match[1]); - } - console.log("typegate counting matches>", line); - return true; - }, null); - - await t.should("successfully deploy on the published version", async () => { - const command = - `meta-old deploy --target dev --threads=4 --allow-dirty --gate http://localhost:${port} -vvv -f func.ts`; - const res = await $`bash -c ${command}` - .cwd(examplesDir.join("typegraphs")) - .env("PATH", `${metaBinDir}:${Deno.env.get("PATH")}`); - console.log(res); - }); - - await stdout.close(); - proc.kill("SIGKILL"); - const status = await proc.status; - console.log({ status }); - - const typegraphs2: string[] = []; - - await t.should("upgrade the typegate to the current version", async () => { - const port = String(t.port + 2); - const proc = new Deno.Command("meta-full", { - args: [ - "typegate", - `--main-url`, - import.meta.resolve("../../../src/typegate/src/main.ts"), - `--import-map-url`, - import.meta.resolve("../../../import_map.json"), - ], - env: { - ...Deno.env.toObject(), - TG_SECRET: tgSecret, - TG_ADMIN_PASSWORD: "password", - TMP_DIR: typegateTempDir, - TG_PORT: `${port}`, - // TODO should not be necessary - VERSION: previousVersion, - ...syncEnvs, - }, - stdout: "piped", - }).spawn(); - - const stdout = new Lines(proc.stdout); - - await stdout.readWhile((line) => { - console.log("typegate>", line); - const match = $.stripAnsi(line).match(/reloaded addition: (.+)/); - if (match) { - typegraphs2.push(match[1]); - } - return !line.includes(`typegate ready on :${port}`); - }); - - await stdout.close(); - proc.kill("SIGKILL"); - const status = await proc.status; - console.log({ status }); - }); - - console.log({ typegraphs: typegraphs.sort() }); - - await t.should("have the same typegraphs", () => { - assertEquals(typegraphs.sort(), typegraphs2.sort()); - }); - - await Deno.remove(typegateTempDir, { recursive: true }); - }, -); - -Meta.test( - { - name: "published SDK tests", - async setup() { - await clearSyncData(syncConfig); - await setupSync(syncConfig); - }, - async teardown() { - await clearSyncData(syncConfig); - }, - }, - async (t) => { - let publishedBin = ""; - await t.should("download published cli (fat version)", async () => { - publishedBin = await downloadAndExtractCli(previousVersion); - }); - - const metaBinDir = $.path(publishedBin).parent()!.toString(); - - const tmpDir = $.path(t.tempDir); - const tgSecret = encodeBase64( - globalThis.crypto.getRandomValues(new Uint8Array(64)), - ); - - const typegateTempDir = await tmpDir.join(".metatype").ensureDir(); - - const port = String(t.port - 10); - - const proc = $`bash -c 'meta-old typegate -vvvv'` - .env({ - PATH: `${metaBinDir}:${Deno.env.get("PATH")}`, - TG_SECRET: tgSecret, - TG_ADMIN_PASSWORD: "password", - TMP_DIR: typegateTempDir.toString(), - TG_PORT: `${port}`, - // TODO should not be necessary - VERSION: previousVersion, - DEBUG: "true", - ...syncEnvs, - }) - .stdout("piped") - .noThrow() - .spawn(); - - const stdout = new Lines(proc.stdout()); - console.log("waiting on typegate to be ready"); - - await stdout.readWhile((line) => { - console.error("typegate>", line); - return !line.includes(`typegate ready on ${port}`); - }); - - const tgsDir = $.path( - await newTempDir({ - dir: undefined, - }), - ); - // t.addCleanup(() => $.removeIfExists(tgsDir)); - - await tgsDir.join("metatype.yml").writeText(` -typegates: - dev: - url: "http://localhost:${port}" - username: admin - password: password - secrets: - roadmap-func: - POSTGRES: "postgresql://postgres:password@localhost:5432/db?schema=roadmap_func2" - BASIC_andim: hunter2 - -typegraphs: - materializers: - prisma: - migrations_path: "migrations" -`); - // FIXME: enable after 0.5.0.-rc.8 releases - /* await t.should("work with JSR npm", async () => { - const npmJsrDir = await tgsDir.join("npm_jsr").ensureDir(); - await $`pnpm init`.cwd(npmJsrDir); - await $`pnpm --package=jsr dlx jsr add @typegraph/sdk@${PUBLISHED_VERSION}` - .cwd( - npmJsrDir, - ); - await $.co([ - $.path("examples/typegraphs/func.ts").copy(npmJsrDir.join("tg.ts")), - $.path("examples/typegraphs/scripts").copyToDir(npmJsrDir), - $.path("examples/templates/node/tsconfig.json").copyToDir(npmJsrDir), - npmJsrDir - .join("package.json") - .readJson() - .then((pkg) => - npmJsrDir - .join("package.json") - .writeJson({ ...(pkg as object), type: "module" }) - ), - ]); - - const command = - `meta-old deploy --target dev --allow-dirty --gate http://localhost:${port} -vvv -f tg.ts`; - await $`bash -c ${command}` - .cwd(npmJsrDir) - .env("PATH", `${metaBinDir}:${Deno.env.get("PATH")}`) - .env("MCLI_LOADER_CMD", "pnpm --package=tsx dlx tsx") - .env("RUST_LOG", "trace"); - }); - - await t.should("work with JSR deno", async () => { - const denoJsrDir = await tgsDir.join("deno_jsr").ensureDir(); - await denoJsrDir.join("deno.json").writeJson({}); - await $`bash -c 'deno add @typegraph/sdk@${PUBLISHED_VERSION}'`.cwd( - denoJsrDir, - ); - await $.co([ - $.path("examples/typegraphs/func.ts").copy(denoJsrDir.join("tg.ts")), - $.path("examples/typegraphs/scripts").copyToDir(denoJsrDir), - ]); - - const command = - `meta-old deploy --target dev --allow-dirty --gate http://localhost:${port} -vvv -f tg.ts`; - await $`bash -c ${command}` - .cwd(denoJsrDir) - .env("PATH", `${metaBinDir}:${Deno.env.get("PATH")}`) - // FIXME: rename to deno.jsonc on bump 0.4.9 - .env("MCLI_LOADER_CMD", `deno run -A --config deno.json`) - .env("RUST_LOG", "trace"); - }); */ - - await t.should("work with pypa", async () => { - const pypaDir = await tgsDir.join("pypa").ensureDir(); - await $ - .raw`poetry init -n --python=${PYTHON_VERSION} --dependency=typegraph:${PUBLISHED_VERSION}` - .cwd( - pypaDir, - ); - await $.co([ - pypaDir.join("README.md").ensureFile(), - $`bash -sx` - .stdinText([ - `python3 -m venv .venv`, - `source .venv/bin/activate`, - `poetry install --no-root`, - ].join("\n")) - .cwd( - pypaDir, - ), - $.path("examples/typegraphs/func.py").copy(pypaDir.join("tg.py")), - $.path("examples/typegraphs/scripts").copyToDir(pypaDir), - ]); - - const command = `source .venv/bin/activate &&` + - ` ${metaBinDir}/meta-old deploy --target dev --allow-dirty --gate http://localhost:${port} -vvv -f tg.py`; - await $`bash -c ${command}` - .cwd(pypaDir) - .env("PATH", `${metaBinDir}:${Deno.env.get("PATH")}`) - .env("MCLI_LOADER_PY", `poetry run python`) - .env("RUST_LOG", "trace"); - }); - - proc.kill("SIGKILL"); - const status = await proc; - console.log({ status }); - await stdout.close(); - }, -); diff --git a/tests/e2e/published/sdk_test.ts b/tests/e2e/published/sdk_test.ts new file mode 100644 index 000000000..1aa3a889b --- /dev/null +++ b/tests/e2e/published/sdk_test.ts @@ -0,0 +1,177 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +import { Meta } from "test-utils/mod.ts"; +import { $ } from "@local/tools/deps.ts"; +import { PUBLISHED_VERSION, PYTHON_VERSION } from "@local/tools/consts.ts"; +import { encodeBase64 } from "@std/encoding/base64"; +import { Lines } from "test-utils/process.ts"; +import { newTempDir } from "test-utils/dir.ts"; +import { downloadAndExtractCli } from "./utils.ts"; +import { Config } from "./config.ts"; + +const testConfig = new Config(12, "published-sdk-test"); + +// const previousVersion = PUBLISHED_VERSION; +const previousVersion = "0.4.10"; + +Meta.test.only( + { + name: "published SDK tests", + async setup() { + await testConfig.clearSyncData(); + await testConfig.setupSync(); + }, + async teardown() { + await testConfig.clearSyncData(); + }, + }, + async (t) => { + // const previousVersion = PUBLISHED_VERSION; + let publishedBin = ""; + await t.should("download published cli (fat version)", async () => { + publishedBin = await downloadAndExtractCli(previousVersion); + }); + + const metaBinDir = $.path(publishedBin).parent()!.toString(); + + const tmpDir = $.path(t.tempDir); + const tgSecret = encodeBase64( + globalThis.crypto.getRandomValues(new Uint8Array(64)), + ); + + const typegateTempDir = await tmpDir.join(".metatype").ensureDir(); + + const port = String(t.port - 10); + + const proc = $`bash -c 'meta-old typegate -vvvv'` + .env({ + PATH: `${metaBinDir}:${Deno.env.get("PATH")}`, + TG_SECRET: tgSecret, + TG_ADMIN_PASSWORD: "password", + TMP_DIR: typegateTempDir.toString(), + TG_PORT: `${port}`, + // TODO should not be necessary + VERSION: previousVersion, + DEBUG: "true", + ...testConfig.syncEnvs, + }) + .stdout("piped") + .noThrow() + .spawn(); + + const stdout = new Lines(proc.stdout()); + console.log("waiting on typegate to be ready"); + + await stdout.readWhile((line) => { + console.error("typegate>", line); + return !line.includes(`typegate ready on ${port}`); + }); + + const tgsDir = $.path( + await newTempDir({ + dir: undefined, + }), + ); + // t.addCleanup(() => $.removeIfExists(tgsDir)); + + await tgsDir.join("metatype.yml").writeText(` +typegates: + dev: + url: "http://localhost:${port}" + username: admin + password: password + secrets: + roadmap-func: + POSTGRES: "postgresql://postgres:password@localhost:5432/db?schema=roadmap_func2" + BASIC_andim: hunter2 + +typegraphs: + materializers: + prisma: + migrations_path: "migrations" +`); + + // await t.should("work with JSR npm", async () => { + // const npmJsrDir = await tgsDir.join("npm_jsr").ensureDir(); + // await $`pnpm init`.cwd(npmJsrDir); + // await $`pnpm --package=jsr dlx jsr add @typegraph/sdk@${previousVersion}` + // .cwd( + // npmJsrDir, + // ); + // await $.co([ + // $.path("examples/typegraphs/func.ts").copy(npmJsrDir.join("tg.ts")), + // $.path("examples/typegraphs/scripts").copyToDir(npmJsrDir), + // $.path("examples/templates/node/tsconfig.json").copyToDir(npmJsrDir), + // npmJsrDir + // .join("package.json") + // .readJson() + // .then((pkg) => + // npmJsrDir + // .join("package.json") + // .writeJson({ ...(pkg as object), type: "module" }) + // ), + // ]); + // + // const command = + // `meta-old deploy --target dev --allow-dirty --gate http://localhost:${port} -vvv -f tg.ts`; + // await $`bash -c ${command}` + // .cwd(npmJsrDir) + // .env("PATH", `${metaBinDir}:${Deno.env.get("PATH")}`) + // .env("MCLI_LOADER_CMD", "pnpm --package=tsx dlx tsx") + // .env("RUST_LOG", "trace"); + // }); + + // await t.should("work with JSR deno", async () => { + // const denoJsrDir = await tgsDir.join("deno_jsr").ensureDir(); + // await denoJsrDir.join("deno.json").writeJson({}); + // await $`bash -c 'deno add @typegraph/sdk@${previousVersion}'`.cwd( + // denoJsrDir, + // ); + // await $.co([ + // $.path("examples/typegraphs/func.ts").copy(denoJsrDir.join("tg.ts")), + // $.path("examples/typegraphs/scripts").copyToDir(denoJsrDir), + // ]); + // + // const command = + // `meta-old deploy --target dev --allow-dirty --gate http://localhost:${port} -vvv -f tg.ts`; + // await $`bash -c ${command}` + // .cwd(denoJsrDir) + // .env("PATH", `${metaBinDir}:${Deno.env.get("PATH")}`) + // // FIXME: rename to deno.jsonc on bump 0.4.9 + // .env("MCLI_LOADER_CMD", `deno run -A --config deno.json`) + // .env("RUST_LOG", "trace"); + // }); + // + // await t.should("work with pypa", async () => { + // const pypaDir = await tgsDir.join("pypa").ensureDir(); + // await $ + // .raw`poetry init -n --python=${PYTHON_VERSION} --dependency=typegraph:${previousVersion}` + // .cwd( + // pypaDir, + // ); + // await $.co([ + // pypaDir.join("README.md").ensureFile(), + // $`bash -c 'python3 -m venv .venv && source .venv/bin/activate && poetry install --no-root'` + // .cwd( + // pypaDir, + // ), + // $.path("examples/typegraphs/func.py").copy(pypaDir.join("tg.py")), + // $.path("examples/typegraphs/scripts").copyToDir(pypaDir), + // ]); + // + // const command = `source .venv/bin/activate &&` + + // ` ${metaBinDir}/meta-old deploy --target dev --allow-dirty --gate http://localhost:${port} -vvv -f tg.py`; + // await $`bash -c ${command}` + // .cwd(pypaDir) + // .env("PATH", `${metaBinDir}:${Deno.env.get("PATH")}`) + // .env("MCLI_LOADER_PY", `poetry run python`) + // .env("RUST_LOG", "trace"); + // }); + + proc.kill("SIGKILL"); + const status = await proc; + console.log({ status }); + await stdout.close(); + }, +); diff --git a/tests/e2e/published/typegate_upgrade_test.ts b/tests/e2e/published/typegate_upgrade_test.ts new file mode 100644 index 000000000..59d0c240c --- /dev/null +++ b/tests/e2e/published/typegate_upgrade_test.ts @@ -0,0 +1,194 @@ +import { Meta } from "test-utils/mod.ts"; +import { $ } from "@local/tools/deps.ts"; +import { encodeBase64 } from "@std/encoding/base64"; +import { newTempDir } from "test-utils/dir.ts"; +import { Config } from "./config.ts"; +import { downloadAndExtractCli } from "./utils.ts"; +import { Lines } from "test-utils/process.ts"; +import { assertEquals } from "@std/assert"; +import { LATEST_RELEASE_VERSION } from "@local/tools/consts.ts"; + +// TODO remove after the next release +// The build.rs script now uses a META_CMD env var allowing us +// to use meta-old +const disabled: string[] = [ + "metagen-rs.ts", +]; + +const testConfig = new Config(13, "typegate-upgrade-test"); + +const previousVersion = LATEST_RELEASE_VERSION; + +// This also tests the published NPM version of the SDK +Meta.test( + { + name: "typegate upgrade", + async setup() { + await testConfig.clearSyncData(); + await testConfig.setupSync(); + }, + async teardown() { + await testConfig.clearSyncData(); + }, + }, + async (t) => { + let publishedBin = ""; + await t.should("download published cli (fat version)", async () => { + publishedBin = await downloadAndExtractCli(previousVersion); + }); + + const metaBinDir = $.path(publishedBin).parent()!.toString(); + const tgSecret = encodeBase64( + globalThis.crypto.getRandomValues(new Uint8Array(64)), + ); + + const typegateTempDir = await newTempDir(); + const repoDir = await newTempDir(); + const examplesDir = $.path( + await newTempDir({ + dir: undefined, + }), + ); + t.addCleanup(async () => { + await $.co([ + $.removeIfExists(typegateTempDir), + $.removeIfExists(repoDir), + // $.removeIfExists(examplesDir), + ]); + }); + + const port = String(t.port + 1); + + const proc = new Deno.Command("meta-old", { + args: ["typegate"], + env: { + ...Deno.env.toObject(), + LOG_LEVEL: "DEBUG", + PATH: `${metaBinDir}:${Deno.env.get("PATH")}`, + TG_SECRET: tgSecret, + TG_ADMIN_PASSWORD: "password", + TMP_DIR: typegateTempDir, + TG_PORT: port, + // TODO should not be necessary + VERSION: previousVersion, + ...testConfig.syncEnvs, + }, + stdout: "piped", + }).spawn(); + + await t.should( + "download example typegraphs for the published version", + async () => { + const tag = `v${previousVersion}`; + + // FIXME: cache across test runs + await $`git clone https://github.com/metatypedev/metatype.git --depth 1 --branch ${tag}` + .cwd(repoDir) + .stdout("piped") + .stderr("piped") + .printCommand(); + + await $.path(repoDir).join("metatype/examples").copy(examplesDir, { + overwrite: true, + }); + const typegraphsDir = examplesDir.join("typegraphs"); + for await (const entry of typegraphsDir.readDir()) { + const path = typegraphsDir.relative(entry.path); + if (disabled.includes(path.toString())) { + await entry.path.remove().catch((_e) => {}); + } + } + + // NOTE: we clean out the deno.json used by the examples + // before adding the published version + // by default @typegraph/sdk/ needs that trailing slash + // due to https://github.com/WICG/import-maps?tab=readme-ov-file#packages-via-trailing-slashes + await examplesDir.join("deno.json").writeJson({}); + await $.raw`bash -c 'deno add @typegraph/sdk@${previousVersion}'` + .cwd(examplesDir) + .stdout("inherit") + .printCommand(); + }, + ); + + const typegraphs: string[] = []; + + const stdout = new Lines(proc.stdout); + await stdout.readWhile((line) => { + console.log(`typegate>`, line); + return !line.includes(`typegate ready on ${port}`); + }); + stdout.readWhile((line) => { + const match = line.match(/Initializing engine '(.+)'/); + if (match) { + typegraphs.push(match[1]); + } + console.log("typegate counting matches>", line); + return true; + }, null); + + await t.should("successfully deploy on the published version", async () => { + const command = + `meta-old deploy --target dev --threads=4 --allow-dirty --gate http://localhost:${port} -vvv`; + const res = await $`bash -c ${command}` + .cwd(examplesDir.join("typegraphs")) + .env("PATH", `${metaBinDir}:${Deno.env.get("PATH")}`); + console.log(res); + }); + + await stdout.close(); + proc.kill("SIGKILL"); + const status = await proc.status; + console.log({ status }); + + const typegraphs2: string[] = []; + + await t.should("upgrade the typegate to the current version", async () => { + const port = String(t.port + 2); + const proc = new Deno.Command("meta-full", { + args: [ + "typegate", + `--main-url`, + import.meta.resolve("../../../src/typegate/src/main.ts"), + `--import-map-url`, + import.meta.resolve("../../../import_map.json"), + ], + env: { + ...Deno.env.toObject(), + TG_SECRET: tgSecret, + TG_ADMIN_PASSWORD: "password", + TMP_DIR: typegateTempDir, + TG_PORT: `${port}`, + // TODO should not be necessary + VERSION: previousVersion, + ...testConfig.syncEnvs, + }, + stdout: "piped", + }).spawn(); + + const stdout = new Lines(proc.stdout); + + await stdout.readWhile((line) => { + console.log("typegate>", line); + const match = $.stripAnsi(line).match(/reloaded addition: (.+)/); + if (match) { + typegraphs2.push(match[1]); + } + return !line.includes(`typegate ready on :${port}`); + }); + + await stdout.close(); + proc.kill("SIGKILL"); + const status = await proc.status; + console.log({ status }); + }); + + console.log({ typegraphs: typegraphs.sort() }); + + await t.should("have the same typegraphs", () => { + assertEquals(typegraphs.sort(), typegraphs2.sort()); + }); + + await Deno.remove(typegateTempDir, { recursive: true }); + }, +); diff --git a/tests/e2e/published/utils.ts b/tests/e2e/published/utils.ts new file mode 100644 index 000000000..527046344 --- /dev/null +++ b/tests/e2e/published/utils.ts @@ -0,0 +1,76 @@ +import { $ } from "@local/tools/deps.ts"; +import { projectDir } from "@local/tools/utils.ts"; + +import { download } from "download"; +import { Untar } from "@std/archive/untar"; +import { copy } from "@std/io/copy"; +import { encodeBase64 } from "@std/encoding/base64"; +import { readerFromStreamReader } from "@std/io/reader-from-stream-reader"; + +const tempDir = $.path(projectDir).join("tmp"); + +function getAssetName(version: string) { + return `meta-cli-v${version}-${Deno.build.target}`; +} + +async function checkMetaBin(path: typeof tempDir, version: string) { + try { + if (!(await path.exists())) { + return false; + } + const res = await $`bash -c 'meta-old --version'` + .env("PATH", `${path.parent()!.toString()}:${Deno.env.get("PATH")}`) + .stdout("piped"); + if (res.stdout.includes(version)) { + return true; + } + throw new Error(`version mismatch: ${res.stdout}`); + } catch (e) { + console.error(e); + return false; + } +} + +// download the fat version of the cli on the latest stable release +export async function downloadAndExtractCli(version: string) { + const name = getAssetName(version); + const extractTargetDir = tempDir.join(name); + const metaBin = extractTargetDir.join("meta-old"); + if (await checkMetaBin(metaBin, version)) { + return metaBin.toString(); + } + const url = + `https://github.com/metatypedev/metatype/releases/download/v${version}/${name}.tar.gz`; + console.log("Downloading from", url); + const archiveName = `${name}.tar.gz`; + const _fileObj = await download(url, { + file: archiveName, + dir: tempDir.toString(), + }); + const archivePath = tempDir.join(archiveName); + using file = await Deno.open(archivePath.toString()); + const reader = file.readable.pipeThrough(new DecompressionStream("gzip")); + const untar = new Untar(readerFromStreamReader(reader.getReader())); + + await extractTargetDir.ensureDir(); + + for await (const entry of untar) { + if (entry.fileName !== "meta") { + throw new Error("unexpected"); + } + using target = await Deno.open(metaBin.toString(), { + create: true, + write: true, + mode: 0o755, + }); + const res = await copy(entry, target); + console.log(`successfully written ${res} bytes`); + } + + await Deno.remove(archivePath.toString()); + + if (!(await checkMetaBin(metaBin, version))) { + throw new Error("unexpected"); + } + return metaBin.toString(); +} diff --git a/tools/consts.ts b/tools/consts.ts index d5e494bc4..cc4e0e65d 100644 --- a/tools/consts.ts +++ b/tools/consts.ts @@ -1,8 +1,9 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 -export const METATYPE_VERSION = "0.5.0-rc.9"; -export const PUBLISHED_VERSION = "0.5.0-rc.8"; +export const CURRENT_VERSION = "0.5.0"; +export const LATEST_RELEASE_VERSION = "0.4.10"; +export const LATEST_PRE_RELEASE_VERSION = "0.5.0-rc.9"; export const GHJK_VERSION = "v0.2.1"; export const GHJK_ACTION_VERSION = "318209a9d215f70716a4ac89dbeb9653a2deb8bc"; export const RUST_VERSION = "1.80.1"; @@ -23,17 +24,17 @@ export const sedLockLines: Record = { [/([\s-]+uses:\s+metatypedev\/setup-ghjk@).+()/, GHJK_ACTION_VERSION], ], "tests/**/*.snap": [ - [/(\s*static\s*MT_VERSION:\s*&str\s*=\s*").+(";)/, METATYPE_VERSION], + [/(\s*static\s*MT_VERSION:\s*&str\s*=\s*").+(";)/, CURRENT_VERSION], ], "src/typegraph/python/typegraph/__init__.py": [ - ['(version = ").+(")', METATYPE_VERSION], + ['(version = ").+(")', CURRENT_VERSION], ], "src/typegraph/core/src/global_store.rs": [ - [/(\s{4}pub static SDK_VERSION.+=\s?").*(".+;)/, METATYPE_VERSION], + [/(\s{4}pub static SDK_VERSION.+=\s?").*(".+;)/, CURRENT_VERSION], ], "src/typegraph/python/pyproject.toml": [['(description = ").+(")', TAGLINE]], "**/Cargo.toml": [ - [/^(version = ").+(")/, METATYPE_VERSION], + [/^(version = ").+(")/, CURRENT_VERSION], ['(description = ").+(")', TAGLINE], [ /([\w-]+\s*=\s*\{\s*git\s*=\s*"https:\/\/github\.com\/prisma\/prisma-engines"\s*,\s*tag\s*=\s*").+("\s*\})/, @@ -47,7 +48,7 @@ export const sedLockLines: Record = { ['(wasmtime-wasi = ").+(")', WASMTIME_VERSION], ], "src/typegraph/deno/deno.json": [ - [/(\s*"version"\s*:\s*").+(",?)/, METATYPE_VERSION], + [/(\s*"version"\s*:\s*").+(",?)/, CURRENT_VERSION], ], "tools/deps.ts": [[/(.*\/metatypedev\/ghjk\/)[^\/]*(\/.*)/, GHJK_VERSION]], "tools/cross.Dockerfile": [["(ARG GHJK_VERSION=).*()", GHJK_VERSION]], @@ -57,7 +58,7 @@ export const sedLockLines: Record = { ["(ARG GHJK_VERSION=).*()", GHJK_VERSION], ], "src/typegate/src/runtimes/wit_wire/mod.ts": [ - [/(const\s+METATYPE_VERSION = ").*(";)/, METATYPE_VERSION], + [/(const\s+METATYPE_VERSION = ").*(";)/, CURRENT_VERSION], ], "src/typegate/src/typegraph/versions.ts": [ ['(const typegraphVersion = ").*(";)', TYPEGRAPH_VERSION], @@ -74,22 +75,22 @@ export const sedLockLines: Record = { ], "docs/metatype.dev/docusaurus.config.js": [['( tagline: ").+(",)', TAGLINE]], "**/pyproject.toml": [ - ['(version = ").+(")', METATYPE_VERSION], + ['(version = ").+(")', CURRENT_VERSION], [/(wasmtime = "\^).+(")/, WASMTIME_PY_VERSION], ], "examples/templates/**/compose.yml": [ - ["( image: ghcr.io/metatypedev/typegate:v).+()", METATYPE_VERSION], + ["( image: ghcr.io/metatypedev/typegate:v).+()", CURRENT_VERSION], ], "examples/templates/**/pyproject.toml": [ - ['(typegraph = ").+(")', METATYPE_VERSION], + ['(typegraph = ").+(")', CURRENT_VERSION], ], "examples/templates/**/package.json": [ - [/(\s*"@typegraph\/sdk"\s*:\s*"\^).+(",?)/, METATYPE_VERSION], + [/(\s*"@typegraph\/sdk"\s*:\s*"\^).+(",?)/, CURRENT_VERSION], ], "examples/templates/**/*.ts": [ [ /(import\s+.+\s+from "jsr:@typegraph\/sdk@)[^\/]+(\/.+";)/, - METATYPE_VERSION, + CURRENT_VERSION, ], ], "CONTRIBUTING.md": [[/(GHJK_VERSION=").*(")/, GHJK_VERSION]], diff --git a/tools/deps.ts b/tools/deps.ts index 6ebd60a1c..e14846d04 100644 --- a/tools/deps.ts +++ b/tools/deps.ts @@ -46,6 +46,7 @@ export { red, yellow, } from "jsr:@std/fmt@^1.0.0/colors"; +export { assert } from "jsr:@std/assert@^1.0.3"; export { format as formatDuration } from "jsr:@std/fmt@^1.0.0/duration"; export { mergeReadableStreams, TextLineStream } from "jsr:@std/streams@1"; export type {} from "jsr:@std/path@^1.0.2"; diff --git a/tools/jsr/deno2node.ts b/tools/jsr/deno2node.ts index 6254e357e..1fc30fa5a 100644 --- a/tools/jsr/deno2node.ts +++ b/tools/jsr/deno2node.ts @@ -3,7 +3,7 @@ import { dnt, expandGlobSync, join } from "../deps.ts"; import { copyFilesAt, removeExtension } from "../utils.ts"; -import { METATYPE_VERSION, SDK_PACKAGE_NAME_TS, TAGLINE } from "../consts.ts"; +import { CURRENT_VERSION, SDK_PACKAGE_NAME_TS, TAGLINE } from "../consts.ts"; import { fromRoot, outDir } from "./common.ts"; import { srcDir } from "./common.ts"; @@ -56,7 +56,7 @@ await dnt.build({ packageManager: "pnpm", package: { name: SDK_PACKAGE_NAME_TS, - version: METATYPE_VERSION, + version: CURRENT_VERSION, description: TAGLINE, license: "MPL-2.0", repository: { diff --git a/tools/jsr/jsr-gen.ts b/tools/jsr/jsr-gen.ts index 00bb23b62..4aed86de0 100644 --- a/tools/jsr/jsr-gen.ts +++ b/tools/jsr/jsr-gen.ts @@ -1,7 +1,7 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 -import { METATYPE_VERSION, SDK_PACKAGE_NAME_TS } from "../consts.ts"; +import { CURRENT_VERSION, SDK_PACKAGE_NAME_TS } from "../consts.ts"; import { $, existsSync, expandGlob, join } from "../deps.ts"; import { copyFilesAt } from "../utils.ts"; import { removeExtension } from "../utils.ts"; @@ -57,7 +57,7 @@ Deno.writeTextFileSync( JSON.stringify( { name: SDK_PACKAGE_NAME_TS, - version: METATYPE_VERSION, + version: CURRENT_VERSION, // ungitignore // https://jsr.io/docs/troubleshooting#excluded-module-error publish: { diff --git a/tools/tasks/lock.ts b/tools/tasks/lock.ts index b6803b742..3bd3b1b2a 100644 --- a/tools/tasks/lock.ts +++ b/tools/tasks/lock.ts @@ -1,13 +1,22 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 -import { copyLock, type DenoTaskDefArgs, parseArgs, sedLock } from "../deps.ts"; +import { + assert, + copyLock, + type DenoTaskDefArgs, + parseArgs, + sedLock, + semver, +} from "../deps.ts"; import * as consts from "../consts.ts"; export default { "lock-sed": { desc: "Update versions", fn: async ($) => { + validateVersions(); + const args = parseArgs(Deno.args, { boolean: ["check"], default: { version: false, check: false }, @@ -50,3 +59,30 @@ export default { }, }, } satisfies Record; + +export function validateVersions() { + const currentVersion = semver.parse(consts.CURRENT_VERSION); + const latestRelease = semver.parse(consts.LATEST_RELEASE_VERSION); + const latestPreRelease = consts.LATEST_PRE_RELEASE_VERSION == null + ? null + : semver.parse(consts.LATEST_PRE_RELEASE_VERSION); + + const prerelease = currentVersion.prerelease ?? []; + const isPreRelease = prerelease.length > 0; + + if (!isPreRelease || (isPreRelease && prerelease[1] == 0)) { + assert(latestPreRelease == null, "expected no latest pre-release version"); + } + + if (isPreRelease) { + assert( + semver.greaterThan(currentVersion, latestPreRelease!), + "expected current version to be greater than latest pre-release version", + ); + } + + assert( + semver.greaterThan(currentVersion, latestRelease), + "expected current version to be greater than latest release version", + ); +}