Skip to content

Commit

Permalink
fix(cli): deployment after reloading config (#917)
Browse files Browse the repository at this point in the history
<!--
Pull requests are squashed and merged using:
- their title as the commit message
- their description as the commit body

Having a good title and description is important for the users to get
readable changelog.
-->

<!-- 1. Explain WHAT the change is about -->

- Fixes typegraph deployment bug using `meta dev` after changing
`metatype.yml` and added more watcher tests.


<!-- 3. Explain HOW users should update their code -->

#### Migration notes

---

- [x] The change comes with new or modified tests
- [ ] Hard-to-understand functions have explanatory comments
- [ ] End-user documentation is updated to reflect the change
  • Loading branch information
luckasRanarison authored Nov 14, 2024
1 parent f95c122 commit 0273360
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 46 deletions.
16 changes: 7 additions & 9 deletions src/meta-cli/src/deploy/actors/task_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,16 +284,14 @@ impl<A: TaskAction + 'static> Actor for TaskManager<A> {

fn stopping(&mut self, ctx: &mut Self::Context) -> Running {
match &self.stop_reason {
Some(reason) => {
if matches!(reason, StopReason::Restart) {
self.watcher_addr = self
.init_params
.start_source(ctx.address(), self.task_generator.clone());
Running::Continue
} else {
Running::Stop
}
Some(StopReason::Restart) => {
self.stop_reason.take();
self.watcher_addr = self
.init_params
.start_source(ctx.address(), self.task_generator.clone());
Running::Continue
}
Some(_) => Running::Stop,
None => Running::Continue,
}
}
Expand Down
135 changes: 102 additions & 33 deletions tests/e2e/cli/watch_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,75 +2,144 @@
// SPDX-License-Identifier: MPL-2.0

import * as path from "@std/path";
import { assert } from "@std/assert";
import { Meta } from "test-utils/mod.ts";
import { MetaTest } from "test-utils/test.ts";
import { killProcess, Lines } from "test-utils/process.ts";
import { makeMetaCliTest } from "test-utils/meta.ts";

const typegraphConfig = `
typegraphs:
typescript:
include: "api/example.ts"`;
include: "api/deps.ts"`;

async function setupDirectory(t: MetaTest, dir: string) {
console.log("Preparing test directory...");

await t.shell([
"bash",
"-c",
`
rm -rf ./tmp && mkdir -p tmp/deps
meta new --template deno ${dir}
cp ./e2e/cli/typegraphs/deps.ts ${path.join(dir, "api", "example.ts")}
meta new --template python ${dir}
cp ./e2e/cli/typegraphs/deps.ts ${path.join(dir, "api")}
cp ./e2e/cli/typegraphs/deps.ts ${path.join(dir, "api", "excluded.ts")}
cp ./e2e/cli/artifacts/ops.ts ${path.join(dir, "deps", "ops.ts")}
echo "${typegraphConfig}" >> ${path.join(dir, "metatype.yaml")}
`,
]);
}

Meta.test({ name: "meta dev: watch artifacts" }, async (t) => {
Meta.test({ name: "meta dev: watch typegraphs" }, async (t) => {
const targetDir = path.join(t.workingDir, "tmp");

console.log("Preparing test directory...");

await setupDirectory(t, targetDir);

const metadev = new Deno.Command("meta", {
cwd: targetDir,
args: ["dev", `--gate=http://localhost:${t.port}`],
stderr: "piped",
}).spawn();
const { expectStderr, stderr } = makeMetaCliTest(t, targetDir, [
"dev",
`--gate=http://localhost:${t.port}`,
]);

await t.should("deploy typegraphs", async () => {
// order is not deterministic but there should be two
await expectStderr("successfully deployed typegraph");
await expectStderr("successfully deployed typegraph");
});

const stderr = new Lines(metadev.stderr);
await t.should("watch modified typegraph", async () => {
await t.shell(["bash", "-c", "echo '' >> api/example.py"], {
currentDir: targetDir,
});

await t.should("upload artifact", async () => {
await stderr.readWhile((line) => !line.includes("artifact uploaded"));
await expectStderr('File modified: "api/example.py"');
});

await t.should("deploy typegraph", async () => {
await stderr.readWhile(
(line) => !line.includes("successfully deployed typegraph"),
);
await t.should("re-deploy typegraph", () =>
expectStderr("successfully deployed typegraph example"),
);

await t.should("ignore excluded typegraph", async () => {
await t.shell(["bash", "-c", "echo '' >> api/excluded.ts"], {
currentDir: targetDir,
});

const lines: string[] = [];

try {
await stderr.readWhile((line) => {
lines.push(line);
return true;
}, 3000);
} catch (_) {
// timeout error
} finally {
assert(!lines.join("\n").includes("api/excluded.ts"));
}
});
});

Meta.test({ name: "meta dev: watch metatype.yaml" }, async (t) => {
const targetDir = path.join(t.workingDir, "tmp");

await t.shell(["bash", "-c", "echo '' >> deps/ops.ts"], {
currentDir: targetDir,
const { expectStderr } = makeMetaCliTest(t, targetDir, [
"dev",
`--gate=http://localhost:${t.port}`,
]);

await t.should("deploy typegraphs", async () => {
await expectStderr("successfully deployed typegraph");
await expectStderr("successfully deployed typegraph");
});

await t.should("watch modified file", async () => {
await stderr.readWhile((line) => !line.includes("File modified"));
await t.should("watch modified configuration", async () => {
await t.shell(["bash", "-c", "echo '' >> metatype.yaml"], {
currentDir: targetDir,
});

await expectStderr("metatype configuration file changed");
});

await t.should("re-upload artifact", async () => {
await stderr.readWhile((line) => !line.includes("artifact uploaded"));
await t.should("start restart process", async () => {
await expectStderr("reloading all the typegraphs");
await expectStderr("force stopping active tasks");
await expectStderr("starting discovery");
});

await t.should("re-deploy typegraph", async () => {
await stderr.readWhile(
(line) => !line.includes("successfully deployed typegraph"),
);
await t.should("re-deploy all typegraphs", async () => {
await expectStderr("successfully deployed typegraph");
await expectStderr("successfully deployed typegraph");
});
});

Meta.test({ name: "meta dev: watch artifacts" }, async (t) => {
const targetDir = path.join(t.workingDir, "tmp");

t.addCleanup(async () => {
await stderr.close();
await killProcess(metadev);
await t.shell(["rm", "-rf", targetDir]);
const { expectStderr } = makeMetaCliTest(t, targetDir, [
"dev",
`--gate=http://localhost:${t.port}`,
]);

await t.should("upload artifact", () =>
expectStderr("artifact uploaded: ../deps/ops.ts"),
);
await t.should("deploy typegraph", () =>
expectStderr("successfully deployed typegraph deps"),
);
await t.should("watch modified artifact", async () => {
await t.shell(["bash", "-c", "echo '' >> deps/ops.ts"], {
currentDir: targetDir,
});

await expectStderr('File modified: "deps/ops.ts"');
await expectStderr("-> api/deps.ts");
});

await t.should("re-upload artifact", () =>
expectStderr("artifact uploaded: ../deps/ops.ts"),
);

await t.should("re-deploy typegraph", () =>
expectStderr("successfully deployed typegraph deps"),
);

await t.shell(["rm", "-rf", targetDir]);
});
38 changes: 34 additions & 4 deletions tests/utils/meta.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0.
// SPDX-License-Identifier: MPL-2.0

import { $ } from "@david/dax";
import { killProcess, Lines } from "./process.ts";
import { MetaTest } from "./test.ts";
import { shell, ShellOptions, ShellOutput } from "./shell.ts";

// added to path in dev/test.ts
Expand All @@ -15,10 +18,9 @@ export async function metaCli(
first: string | ShellOptions,
...input: string[]
): Promise<ShellOutput> {
const res =
await (typeof first === "string"
? shell([metaCliExe, first, ...input])
: shell([metaCliExe, ...input], first));
const res = await (typeof first === "string"
? shell([metaCliExe, first, ...input])
: shell([metaCliExe, ...input], first));

return res;
}
Expand Down Expand Up @@ -53,3 +55,31 @@ export async function serialize(
const res = await shell(cmd);
return res.stdout;
}

export function makeMetaCliTest(t: MetaTest, cwd: string, args: string[]) {
const meta = new Deno.Command("meta", {
cwd,
args,
stdout: "piped",
stderr: "piped",
}).spawn();

const stdout = new Lines(meta.stdout);
const stderr = new Lines(meta.stderr);

const expectStdout = async (str: string) => {
await stdout.readWhile((line) => !$.stripAnsi(line).includes(str));
};

const expectStderr = async (str: string) => {
await stderr.readWhile((line) => !$.stripAnsi(line).includes(str));
};

t.addCleanup(async () => {
await stdout.close();
await stderr.close();
await killProcess(meta);
});

return { meta, stdout, stderr, expectStdout, expectStderr };
}

0 comments on commit 0273360

Please sign in to comment.