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
10 changes: 7 additions & 3 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -356,19 +356,23 @@ jobs:
START=$(date +%s)
deno task coverage:ci:shard -- --shard=${{ matrix.shard }}/4 --coverage-dir=coverage-shard-${{ matrix.shard }}
echo "duration=$(($(date +%s) - START))s" >> "$GITHUB_OUTPUT"
- name: Include dependency history integration coverage
- name: Include CLI and dependency history integration coverage
if: matrix.shard == 1
run: |
rm -rf coverage-history
rm -rf coverage-history coverage-cli
deno task test:file --coverage=coverage-history \
tests/integration/semantic-unit-boundary/src/platform/adapters/fs/veryfront/dependency-metadata-history.test.ts \
tests/integration/semantic-unit-boundary/src/platform/adapters/veryfront-api-client/dependency-metadata-history.test.ts \
tests/integration/semantic-unit-boundary/src/transforms/esm/package-registry-metadata-history.test.ts
# The CLI regression changes cwd, so give it a separate process.
deno task test:file --coverage=coverage-cli \
tests/integration/cli/merge-preview.integration.test.ts
deno coverage coverage-history --include=src/ --include=cli/ --exclude=/tests/ --exclude=/__tests__/ --lcov > coverage-history.lcov
deno coverage coverage-cli --include=src/ --include=cli/ --exclude=/tests/ --exclude=/__tests__/ --lcov > coverage-cli.lcov
deno eval '
import { mergeLcovReports } from "./scripts/test/coverage-ci.ts";
const target = "coverage-shard-1/lcov.info";
const reports = await Promise.all([target, "coverage-history.lcov"].map(path => Deno.readTextFile(path)));
const reports = await Promise.all([target, "coverage-history.lcov", "coverage-cli.lcov"].map(path => Deno.readTextFile(path)));
await Deno.writeTextFile(target, mergeLcovReports(reports));
'
- name: Upload unit coverage lcov
Expand Down
2 changes: 1 addition & 1 deletion cli/commands/merge/command-help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ export const mergeHelp: CommandHelp = {
"Requires VERYFRONT_API_TOKEN env var or veryfront.json config",
"A self-hosted apiUrl in veryfront.json or a project .env file needs a token from that same file, or VERYFRONT_API_URL set in your shell or CI environment to confirm the host",
"Use --dry-run to preview which files would be merged",
"Conflicts are reported but must be resolved in Studio",
"--dry-run reports conflicting file paths; resolve those conflicts in Studio",
],
};
8 changes: 4 additions & 4 deletions cli/commands/merge/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ interface MergeResponse {
* Merge preview diff item
*/
interface MergePreviewDiff {
path: string;
has_conflict: boolean;
file_path: string;
has_conflicts: boolean;
}

/**
Expand Down Expand Up @@ -190,13 +190,13 @@ export async function mergeCommand(options: MergeOptions): Promise<void> {
);
spinner.stop();

const conflicts = diffs.filter((d) => d.has_conflict);
const conflicts = diffs.filter((d) => d.has_conflicts);

logInfo(`Would merge ${diffs.length} files from "${branch}" into ${targetName}`);
if (conflicts.length > 0) {
cliLogger.warn(` ${conflicts.length} file(s) have conflicts`);
for (const conflict of conflicts) {
cliLogger.warn(` - ${conflict.path}`);
cliLogger.warn(` - ${conflict.file_path}`);
}
}
return;
Expand Down
1 change: 1 addition & 0 deletions scripts/test/suites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export const UNIT_CWD_FILES: readonly string[] = Object.freeze([
const CWD_MUTATING_TEST_FILES = new Set([
...UNIT_CWD_FILES,
"tests/integration/adapters/shell-adapter.test.ts",
"tests/integration/cli/merge-preview.integration.test.ts",
"tests/integration/cli/mcp/standalone-auth-scaffold.test.ts",
"tests/integration/semantic-unit-boundary/cli/scaffold/missing-parent-race.test.ts",
]);
Expand Down
68 changes: 68 additions & 0 deletions tests/integration/cli/merge-preview.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import "#veryfront/schemas/_test-setup.ts";
import { assertEquals } from "#veryfront/testing/assert.ts";
import { describe, it } from "#veryfront/testing/bdd.ts";
import { withMockFetch } from "#veryfront/testing/mock-fetch.ts";
import { makeTempDir } from "#veryfront/testing/deno-compat.ts";
import { _resetEnvironmentConfig } from "#veryfront/config/environment-config.ts";
import { cliLogger } from "../../../cli/utils/index.ts";
import { mergeCommand } from "../../../cli/commands/merge/command.ts";

describe("merge dry-run REST contract", () => {
it("finds a branch on the next page and reports canonical conflict paths without merging", async () => {
const originalDirectory = Deno.cwd();
const directory = await makeTempDir();
const keys = ["VERYFRONT_API_TOKEN", "VERYFRONT_API_URL", "VERYFRONT_PROJECT_SLUG"];
const previous = keys.map((key) => Deno.env.get(key));
const originalWarn = cliLogger.warn;
const warnings: string[] = [];
const requests: string[] = [];
try {
Deno.chdir(directory);
Deno.env.set("VERYFRONT_API_TOKEN", "<TOKEN>");
Deno.env.set("VERYFRONT_API_URL", "https://control.example.test");
Deno.env.set("VERYFRONT_PROJECT_SLUG", "fixture-project");
_resetEnvironmentConfig();
cliLogger.warn = (...args: unknown[]) => warnings.push(args.map(String).join(" "));
await withMockFetch(async (input, init) => {
const request = input instanceof Request ? input : new Request(input, init);
const url = new URL(request.url);
requests.push(`${request.method} ${url.pathname}${url.search}`);
assertEquals(request.method, "GET");
if (url.pathname === "/projects/fixture-project/branches") {
assertEquals(url.searchParams.get("search"), "fixture");
assertEquals(url.searchParams.get("limit"), "100");
return Response.json(
url.searchParams.has("cursor")
? {
data: [{ id: "branch-id", name: "fixture", project_id: "project-id" }],
page_info: { next: null },
}
: {
data: [{ id: "other-id", name: "fixture-other", project_id: "project-id" }],
page_info: { next: "page-two" },
},
);
}
assertEquals(url.pathname, "/projects/fixture-project/branches/branch-id/merge-preview");
return Response.json({
diffs: [
{ file_path: "app/page.tsx", has_conflicts: true },
{ file_path: "app/layout.tsx", has_conflicts: false },
],
});
}, () => mergeCommand({ branch: "fixture", dryRun: true, force: false }));
assertEquals(requests.length, 3);
assertEquals(requests[1]?.includes("cursor=page-two"), true);
assertEquals(warnings, [" 1 file(s) have conflicts", " - app/page.tsx"]);
} finally {
cliLogger.warn = originalWarn;
Deno.chdir(originalDirectory);
keys.forEach((key, i) => {
if (previous[i] === undefined) Deno.env.delete(key);
else Deno.env.set(key, previous[i]);
});
_resetEnvironmentConfig();
await Deno.remove(directory, { recursive: true });
}
});
});
Loading