repos / starfx

supercharged async flow control library.
git clone https://github.com/neurosnap/starfx.git

commit
54f6fc8
parent
cab4009
author
Jacob Bolda
date
2024-01-30 04:05:41 +0000 UTC
ecosystem CI tests (#36)

* initial CI setup

* fix CI workflow name

* consistent CI workflow name with file name

* script to symlink and CI workflow update to run build

* fix folder name in example dir

* try --refresh-lockfile

* try with npm

* try with --no-lockfile

* try with npm including build

* npm run build

* run as matrix

* don't fail fast

* skip remove of ./npm/node_modules

* do file: install for parcel instead

* keep node_modules for parcel

* matrix out path chunks

* lint fix

* another lint fix

* fix CI name

* install both examples

* dynamically pull matching branch

* nested folder

* quiet script

* throw on failed reponse

* pass branch

* more error context

* bad boop

* name branch step

* lint

* build and test examples

* remove GITHUB_TOKEN

* directly append in script

* add logs

* dump output

* dump output file

* dump step

* .outputs

* lint

* really need to fix my local linter

* add a bit to the readme

* rename .scripts/ to scripts/

* update script references
5 files changed,  +159, -1
A .github/workflows/test-ecosystem.yml
+73, -0
 1@@ -0,0 +1,73 @@
 2+name: test-ecosystem
 3+
 4+on:
 5+  push:
 6+    branches: main
 7+  pull_request:
 8+    branches: main
 9+
10+permissions:
11+  contents: read
12+
13+jobs:
14+  test-ecosystem:
15+    name: ${{ matrix.example.repo }}/${{ matrix.example.folder }}
16+    runs-on: ubuntu-latest
17+    strategy:
18+      fail-fast: false
19+      matrix:
20+        example:
21+          - owner: neurosnap
22+            repo: starfx-examples
23+            folder: vite-react
24+          - owner: neurosnap
25+            repo: starfx-examples
26+            folder: parcel-react
27+          - owner: neurosnap
28+            repo: starfx-examples
29+            folder: tests-rtl
30+    steps:
31+      - name: checkout main repo
32+        uses: actions/checkout@v4
33+        with:
34+          repository: "neurosnap/starfx"
35+          path: "starfx"
36+
37+      - name: setup deno
38+        uses: denoland/setup-deno@v1
39+        with:
40+          deno-version: v1.x
41+
42+      # determines branch and sets it as output available through the `id`
43+      - name: dynamically determine ${{ matrix.example.owner }}/${{ matrix.example.repo }} branch
44+        id: conditionalBranch
45+        run: deno run -A ./starfx/scripts/branch-exists.ts $GITHUB_HEAD_REF neurosnap/starfx-examples
46+
47+      - name: checkout ${{ matrix.example.owner }}/${{ matrix.example.repo }} on ${{ steps.conditionalBranch.outputs.branch }}
48+        uses: actions/checkout@v4
49+        with:
50+          repository: ${{ matrix.example.owner }}/${{ matrix.example.repo }}
51+          path: ${{ matrix.example.repo }}
52+          ref: ${{ steps.conditionalBranch.outputs.branch }}
53+
54+      - name: bundle for npm
55+        run: deno task npm 0.0.0
56+        working-directory: starfx
57+
58+      # install in example repos
59+      - name: install ${{ matrix.example.owner }}/${{ matrix.example.repo }}
60+        working-directory: ${{ matrix.example.repo }}/${{ matrix.example.folder }}
61+        run: npm install
62+
63+      # symlink example repos
64+      - name: symlink built assets
65+        run: deno task sync-build-to install ${{ matrix.example.repo }}/${{ matrix.example.folder }}
66+        working-directory: starfx
67+
68+      # run build and test in example repos
69+      - name: build ${{ matrix.example.owner }}/${{ matrix.example.repo }}
70+        working-directory: ${{ matrix.example.repo }}/${{ matrix.example.folder }}
71+        run: npm run build --if-present
72+      - name: test ${{ matrix.example.owner }}/${{ matrix.example.repo }}
73+        working-directory: ${{ matrix.example.repo }}/${{ matrix.example.folder }}
74+        run: npm run test --if-present
M deno.json
+2, -1
 1@@ -2,7 +2,8 @@
 2   "tasks": {
 3     "types": "deno run --allow-write ./api-type-template.ts",
 4     "npm": "deno run -A ./npm.ts",
 5-    "test": "deno test --allow-env --allow-read"
 6+    "test": "deno test --allow-env --allow-read",
 7+    "sync-build-to": "deno run -A ./scripts/sync.ts"
 8   },
 9   "lint": {
10     "exclude": ["npm/", "examples/"],
A scripts/branch-exists.ts
+52, -0
 1@@ -0,0 +1,52 @@
 2+import { call, main, type Operation } from "./deps.ts";
 3+
 4+await main(function* (): Operation<void> {
 5+  // based on env created from ${{ secrets.GITHUB_TOKEN }} in CI
 6+  const token = Deno.env.get("GITHUB_TOKEN");
 7+  const [branch, ownerRepo] = Deno.args;
 8+  console.dir({ branch, ownerRepo });
 9+
10+  const response = yield* call(
11+    fetch(`https://api.github.com/repos/${ownerRepo}/branches`, {
12+      headers: {
13+        Accept: "application/vnd.github+json",
14+        "X-GitHub-Api-Version": "2022-11-28",
15+        // the token isn't required but helps with rate limiting
16+        ...(token ? { Authorization: `Bearer ${token}` } : {}),
17+      },
18+    }),
19+  );
20+
21+  if (response.ok) {
22+    const branches = yield* call(response.json());
23+    const branchList = branches.map((branch: { name: string }) => branch.name);
24+    // for CI debug purposes
25+    console.dir({ branchList });
26+    // GitHub Actions maintains the step output through a file which you append keys into
27+    //   the path that file is available as an env var
28+    if (Deno.env.get("CI")) {
29+      const output = Deno.env.get("GITHUB_OUTPUT");
30+      if (!output) throw new Error("$GITHUB_OUTPUT is not set");
31+      const encoder = new TextEncoder();
32+      if (branchList.includes(branch)) {
33+        const data = encoder.encode(`branch=${branch}`);
34+        yield* call(Deno.writeFile(output, data, { append: true }));
35+      } else {
36+        const data = encoder.encode("branch=main");
37+        yield* call(Deno.writeFile(output, data, { append: true }));
38+      }
39+    }
40+    // always log out the branch for both CI and local running
41+    if (branchList.includes(branch)) {
42+      console.log(`branch=${branch}`);
43+    } else {
44+      console.log(`branch=main`);
45+    }
46+  } else {
47+    console.error(
48+      `Error trying to fetch https://api.github.com/repos/${ownerRepo}/branches and check for ${branch}`,
49+    );
50+    const text = yield* call(response.text());
51+    throw new Error(text);
52+  }
53+});
A scripts/deps.ts
+2, -0
1@@ -0,0 +1,2 @@
2+export type { Operation } from "https://deno.land/x/effection@3.0.0-beta.3/mod.ts";
3+export { call, main } from "https://deno.land/x/effection@3.0.0-beta.3/mod.ts";
A scripts/sync.ts
+30, -0
 1@@ -0,0 +1,30 @@
 2+import { call, main, type Operation } from "./deps.ts";
 3+
 4+await main(function* (): Operation<void> {
 5+  const [syncMethod, folderFromArgs] = Deno.args;
 6+  const folder = folderFromArgs ?? "starfx-examples/vite-react";
 7+  const dir = `../${folder}/node_modules/starfx`;
 8+  const npmAssets = yield* call(Deno.realPath("./npm"));
 9+
10+  if (syncMethod === "install") {
11+    // parcel doesn't handle symlinks well, do a `file:` install instead
12+    const command = new Deno.Command("npm", {
13+      args: ["add", "starfx@file:../../starfx/npm", "--install-links"],
14+      cwd: `../${folder}`,
15+      stderr: "piped",
16+      stdout: "piped",
17+    });
18+    yield* call(command.output());
19+  } else if (syncMethod === "symlink") {
20+    // this option is primarily for local usage
21+    try {
22+      yield* call(Deno.remove(dir, { recursive: true }));
23+    } catch (_error) {
24+      // assume that the folder does not exist
25+    }
26+
27+    // create a symlink to the `dir` which should allow
28+    // this example to run with the build assets
29+    yield* call(Deno.symlink(npmAssets, dir));
30+  }
31+});