Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feature/latest-elec…
Browse files Browse the repository at this point in the history
…tron-native-superstring
  • Loading branch information
mauricioszabo committed Nov 6, 2023
2 parents 6c7ecd0 + d42bae7 commit 1e057a5
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 7 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/validate-wasm-grammar-prs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Validate WASM Grammar PR Changes
# Since we now want to enforce the rule that any changes to a WASM grammar binary
# file, is accompanied by a change to the `parserSource` key within the
# `grammar.cson` file. This GHA will preform this check for us.

on:
pull_request:
paths:
- '**.wasm'

jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout the Latest Code
uses: actions/checkout@v3
with:
fetch-depth: 0
# Make sure we get all commits, so that we can compare to early commits

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 16

- name: Install dependencies
run: yarn install

- name: Run Validation Script
run: node ./script/validate-wasm-grammar-prs.js
33 changes: 27 additions & 6 deletions packages/pulsar-updater/spec/cache-spec.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
const cache = require("../src/cache.js");

describe("pulsar-updater cache", () => {
beforeEach(() => {
jasmine.useRealClock();
});

it("returns key for path", () => {
let key = cache.cacheKeyForPath("test");
expect(key).toBe("pulsar-updater:test");
});

it("returns expired properly according to date", () => {
let expiry = cache.isItemExpired({ createdOn: Date.now() });
it("returns not expired properly according to date", () => {
let expiry = cache.isItemExpired({ createdOn: Date.now() }, 'some-key');
expect(expiry).toBe(false);
});

it("returns not expired if offline", () => {
spyOn(cache, "online").andReturn(true);
it("returns expired properly according to date", () => {
let expiry = cache.isItemExpired({ createdOn: 0 }, 'some-key');
expect(expiry).toBe(true);
});

let expiry = cache.isItemExpired({ createdOn: 0 });
it("returns not expired properly for last-update-check", () => {
let expiry = cache.isItemExpired({ createdOn: 0 }, 'last-update-check');
expect(expiry).toBe(false);
})
});

if(jasmine.version_.major > 1) {
// TODO: The current version right now is 1.3.1 of jasmine-node
// https://github.com/kevinsawicki/jasmine-node
//
// This is an unmaintained package that tried to implement jasmine for node,
// however we have an official jasmine implementation since then
it("returns not expired if offline", () => {
spyOnProperty(window.navigator, 'onLine').and.returnValue(false);

let expiry = cache.isItemExpired({ createdOn: 0 }, 'some-key');
expect(expiry).toBe(false);
});
}
});
2 changes: 1 addition & 1 deletion packages/pulsar-updater/src/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function getCacheItem(key) {

let cached = JSON.parse(obj);

if (typeof cached === "object" && !isItemExpired(cached)) {
if (typeof cached === "object" && !isItemExpired(cached, key)) {
return JSON.parse(cached.data);
}

Expand Down
148 changes: 148 additions & 0 deletions script/validate-wasm-grammar-prs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
* This script is called via `validate-wasm-grammar-prs.yml`
* It's purpose is to ensure that everytime a `.wasm` file is changed in a PR
* That the `parserSource` key of the grammar that uses that specific `.wasm`
* file is also updated.
* This way we can ensure that the `parserSource` is always accurate, and is
* never forgotten about.
*/

const cp = require("node:child_process");
const path = require("node:path");
const fs = require("node:fs");
const CSON = require("season");

// Change this if you want more logs
let verbose = true;

// Lets first find our common ancestor commit
// This lets us determine the commit where the branch or fork departed from
const commonAncestorCmd = cp.spawnSync("git", [ "merge-base", "origin/master", "HEAD^" ]);

if (commonAncestorCmd.status !== 0 || commonAncestorCmd.stderr.toString().length > 0) {
console.error("Git Command has failed!");
console.error("'git merge-base origin/master HEAD^'");
console.error(commonAncestorCmd.stderr.toString());
process.exit(1);
}

const commit = commonAncestorCmd.stdout.toString().trim();

if (verbose) {
console.log(`Common Ancestor Commit: '${commit}'`);
}

const cmd = cp.spawnSync("git", [ "diff", "--name-only", "-r", "HEAD", commit])

if (cmd.status !== 0 || cmd.stderr.toString().length > 0) {
console.error("Git Command has failed!");
console.error(`'git diff --name-only -r HEAD ${commit}'`);
console.error(cmd.stderr.toString());
process.exit(1);
}

const changedFiles = cmd.stdout.toString().split("\n");
// This gives us an array of the name and path of every single changed file from the last two commits
// Now to check if there's any changes we care about.

if (verbose) {
console.log("Array of changed files between commits:");
console.log(changedFiles);
}

const wasmFilesChanged = changedFiles.filter(element => element.endsWith(".wasm"));

if (wasmFilesChanged.length === 0) {
// No WASM files have been modified. Return success
console.log("No WASM files have been changed.");
process.exit(0);
}

// Now for every single wasm file that's been changed, we must validate those changes
// are also accompanied by a change in the `parserSource` key

for (const wasmFile of wasmFilesChanged) {
const wasmPath = path.dirname(wasmFile);

const files = fs.readdirSync(path.join(wasmPath, ".."));
console.log(`Detected changes to: ${wasmFile}`);

if (verbose) {
console.log("Verbose file check details:");
console.log(wasmFile);
console.log(wasmPath);
console.log(files);
console.log("\n");
}

for (const file of files) {
const filePath = path.join(wasmPath, "..", file);
console.log(`Checking: ${filePath}`);

if (fs.lstatSync(filePath).isFile()) {
const contents = CSON.readFileSync(filePath);

// We now have the contents of one of the grammar files for this specific grammar.
// Since each grammar may contain multiple grammar files, we need to ensure
// that this particular one is using the tree-sitter wasm file that was
// actually changed.
const grammarFile = contents.treeSitter?.grammar ?? "";

if (path.basename(grammarFile) === path.basename(wasmFile)) {
// This grammar uses the WASM file that's changed. So we must ensure our key has also changed
// Sidenote we use `basename` here, since the `wasmFile` will be
// a path relative from the root of the repo, meanwhile `grammarFile`
// will be relative from the file itself

// In order to check the previous state of what the key is, we first must retreive the file prior to this PR
const getPrevFile = cp.spawnSync("git", [ "show", `${commit}:./${filePath}` ]);

if (getPrevFile.status !== 0 || getPrevFile.stderr.toString().length > 0) {
// This can fail for two major reasons
// 1. The `git show` command has returned an error code other than `0`, failing.
// 2. This is a new file, and it failed to find an earlier copy (which didn't exist)
// So that we don't fail brand new TreeSitter grammars, we manually check for number 2

if (getPrevFile.stderr.toString().includes("exists on disk, but not in")) {
// Looks like this file is new. Skip this check
if (verbose) {
console.log("Looks like this file is new. Skipping...");
}
continue;
}

console.error("Git command failed!");
console.error(`'git show ${commit}:./${filePath}'`);
console.error(getPrevFile.stderr.toString());
process.exit(1);
}

fs.writeFileSync(path.join(wasmPath, "..", `OLD-${file}`), getPrevFile.stdout.toString());

const oldContents = CSON.readFileSync(path.join(wasmPath, "..", `OLD-${file}`));
const oldParserSource = oldContents.treeSitter?.parserSource ?? "";
const newParserSource = contents.treeSitter?.parserSource ?? "";

if (newParserSource.length === 0) {
console.error(`Failed to find the new \`parserSource\` within: '${filePath}'`);
console.error(contents.treeSitter);
process.exit(1);
}

if (oldParserSource == newParserSource) {
// The repo and commit is identical! This means it hasn't been updated
console.error(`The \`parserSource\` key of '${filePath}' has not been updated!`);
console.error(`Current key: ${newParserSource} - Old key: ${oldParserSource}`);
process.exit(1);
}

// Else it looks like it has been updated properly
console.log(`Validated \`parserSource\` has been updated within '${filePath}' properly.`);
} else {
if (verbose) {
console.log("This grammar file doesn't use a WASM file that's changed (On the current iteration)");
}
}
}
}
}

0 comments on commit 1e057a5

Please sign in to comment.