Skip to content

Commit ac92cbb

Browse files
fix(ci): move example tests to GitHub Actions to avoid Dagger symlink loop
The examples reference the root package via "../../" which creates symlinks that point back to the project root. When Dagger snapshots the directory, this causes infinite recursion and a "file name too long" panic. This change: - Excludes the examples directory from Dagger entirely - Removes example-related functions from the Dagger module - Adds a new test-examples job in GitHub Actions that runs examples in parallel using a matrix strategy 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 848a46e commit ac92cbb

File tree

2 files changed

+36
-93
lines changed

2 files changed

+36
-93
lines changed

.dagger/src/index.ts

Lines changed: 6 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ import { argument, dag, Directory, func, object, Secret } from "@dagger.io/dagge
22
import type { Container } from "@dagger.io/dagger";
33
import {
44
withTiming,
5-
runNamedParallel,
65
releasePr as sharedReleasePr,
76
githubRelease as sharedGithubRelease,
8-
type NamedResult,
97
} from "@shepherdjerred/dagger-utils";
108

119
const WORKDIR = "/workspace";
@@ -56,55 +54,6 @@ export class AstroOpengraphImages {
5654
});
5755
}
5856

59-
/**
60-
* Test a specific example
61-
*/
62-
@func()
63-
async testExample(
64-
@argument({ defaultPath: "." })
65-
source: Directory,
66-
@argument()
67-
example: string,
68-
): Promise<string> {
69-
return withTiming(`test-example:${example}`, async () => {
70-
const build = this.buildRoot(source);
71-
await build.stdout();
72-
await this.runExampleWithBase(example, build);
73-
return `Example ${example} built successfully.`;
74-
});
75-
}
76-
77-
/**
78-
* Test all examples in parallel
79-
*/
80-
@func()
81-
async testAll(
82-
@argument({ defaultPath: "." })
83-
source: Directory,
84-
): Promise<string> {
85-
return withTiming("test-all-examples", async () => {
86-
const examples = await this.exampleNames(source);
87-
const build = this.buildRoot(source);
88-
await build.stdout();
89-
90-
const results = await runNamedParallel(
91-
examples.map((example) => ({
92-
name: example,
93-
operation: () => this.runExampleWithBase(example, build),
94-
})),
95-
);
96-
97-
const failed = results.filter((r: NamedResult<void>) => !r.success);
98-
if (failed.length > 0) {
99-
const failedNames = failed.map((r: NamedResult<void>) => r.name).join(", ");
100-
throw new Error(`Examples failed: ${failedNames}`);
101-
}
102-
103-
const exampleList = examples.join(", ");
104-
return `All ${examples.length.toString()} examples built successfully: ${exampleList}`;
105-
});
106-
}
107-
10857
/**
10958
* Run the complete CI pipeline
11059
*/
@@ -118,30 +67,11 @@ export class AstroOpengraphImages {
11867

11968
await withTiming("lint", () => deps.withExec(["bun", "run", "lint"]).stdout());
12069

121-
const build = deps.withExec(["bun", "run", "build"]);
122-
await withTiming("build", () => build.stdout());
70+
await withTiming("build", () => deps.withExec(["bun", "run", "build"]).stdout());
12371

12472
await withTiming("test", () => deps.withExec(["bun", "run", "test"]).stdout());
12573

126-
const examples = await this.exampleNames(source);
127-
128-
await withTiming("test-examples", async () => {
129-
const results = await runNamedParallel(
130-
examples.map((example) => ({
131-
name: example,
132-
operation: () => this.runExampleWithBase(example, build),
133-
})),
134-
);
135-
136-
const failed = results.filter((r: NamedResult<void>) => !r.success);
137-
if (failed.length > 0) {
138-
const failedNames = failed.map((r: NamedResult<void>) => r.name).join(", ");
139-
throw new Error(`Examples failed: ${failedNames}`);
140-
}
141-
});
142-
143-
const exampleList = examples.join(", ");
144-
return `CI pipeline completed successfully. Tested ${examples.length.toString()} examples: ${exampleList}`;
74+
return "CI pipeline completed successfully.";
14575
});
14676
}
14777

@@ -282,7 +212,10 @@ export class AstroOpengraphImages {
282212
.withWorkdir(WORKDIR)
283213
.withEnvVariable("CI", "true")
284214
.withDirectory(WORKDIR, source, {
285-
exclude: ["node_modules", "examples/*/node_modules", "**/.astro", "**/.dagger"],
215+
// Examples are excluded because they reference the root package via "../../" which
216+
// creates symlinks that point back to the project root, causing infinite recursion
217+
// when Dagger snapshots the directory. Example tests run in GitHub Actions instead.
218+
exclude: ["node_modules", "examples", "**/.astro", "**/.dagger"],
286219
});
287220
}
288221

@@ -296,24 +229,4 @@ export class AstroOpengraphImages {
296229
private buildRoot(source: Directory): Container {
297230
return this.installDependencies(source).withExec(["bun", "run", "build"]);
298231
}
299-
300-
private async exampleNames(source: Directory): Promise<string[]> {
301-
const examplesDir = source.directory("examples");
302-
const entries = await examplesDir.entries();
303-
return entries
304-
.map((entry) => entry.replace(/\/+$/, "")) // Strip trailing slashes from directory names
305-
.filter((entry) => entry.trim().length > 0)
306-
.sort();
307-
}
308-
309-
private async runExampleWithBase(example: string, base: Container): Promise<void> {
310-
const exampleWorkdir = `${WORKDIR}/examples/${example}`;
311-
// Don't use node_modules cache for examples - local file references (../../) cause stale
312-
// cache entries that fail with ENOENT or module resolution errors.
313-
let container = base.withWorkdir(exampleWorkdir).withExec(["bun", "install", "--frozen-lockfile"]);
314-
315-
container = container.withExec(["bun", "run", "build"]);
316-
317-
await container.stdout();
318-
}
319232
}

.github/workflows/ci.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,36 @@ jobs:
118118
exit $EXIT_CODE
119119
fi
120120
121+
# Example tests run in GHA instead of Dagger because the examples reference
122+
# the root package via "../../" which creates symlinks that cause infinite
123+
# recursion when Dagger snapshots the directory.
124+
test-examples:
125+
runs-on: ubuntu-latest
126+
strategy:
127+
matrix:
128+
example: [custom, preset]
129+
steps:
130+
- name: Checkout repository
131+
uses: actions/checkout@v4
132+
133+
- name: Set up Bun
134+
uses: oven-sh/setup-bun@v2
135+
with:
136+
bun-version: latest
137+
138+
- name: Install root dependencies and build
139+
run: |
140+
bun install --frozen-lockfile
141+
bun run build
142+
143+
- name: Install example dependencies
144+
working-directory: examples/${{ matrix.example }}
145+
run: bun install --frozen-lockfile
146+
147+
- name: Build example
148+
working-directory: examples/${{ matrix.example }}
149+
run: bun run build
150+
121151
permissions:
122152
contents: write
123153
pull-requests: write

0 commit comments

Comments
 (0)