diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d132eb423c0..ffd302b784a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -102,25 +102,6 @@ jobs: name: dist path: dist/* - # Create patch to go from the PR head to the merge commit - - name: Create git patch for merge - if: github.event_name == 'pull_request' - id: diff - run: | - git diff --binary --patch ${{ github.event.pull_request.head.sha }} ${{ github.sha }} > update.diff - if [ -s update.diff ]; then - echo "Diff found, creating a patch to apply later" - cat update.diff - echo "diff=true" >> $GITHUB_OUTPUT - fi - - - name: Upload patch - if: steps.diff.outputs.diff == 'true' - uses: actions/upload-artifact@v3 - with: - name: update.diff - path: update.diff - test: name: Test timeout-minutes: 30 @@ -312,11 +293,8 @@ jobs: run: | PATCH_COUNT=0 for f in $(find ./*.diff/*.diff); do - # Exclude update.diff since we don't want to fail if the PR is just not up to date - if [ "$f" != "./update.diff/update.diff" ]; then - PATCH_COUNT=$((PATCH_COUNT + 1)) - cat $f - fi + PATCH_COUNT=$((PATCH_COUNT + 1)) + cat $f done if [ $PATCH_COUNT -gt 0 ]; then echo "Found $PATCH_COUNT patches, build failed. A self-mutation should happen soon." diff --git a/.github/workflows/mutation.yml b/.github/workflows/mutation.yml index b1a9fe408e1..302a440d5b2 100644 --- a/.github/workflows/mutation.yml +++ b/.github/workflows/mutation.yml @@ -21,8 +21,7 @@ permissions: jobs: mutate: runs-on: ubuntu-latest - # Run if the workflow run is a pull request - if: github.event.workflow_run.conclusion == 'failure' && (!contains(fromJSON('["main", "dev"]'), github.event.workflow_run.head_branch) || github.event.workflow_run.head_repository.fork) + if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'failure' && (!contains(fromJSON('["main", "dev"]'), github.event.workflow_run.head_branch) || github.event.workflow_run.head_repository.fork) steps: - name: Download artifacts id: download-artifacts @@ -57,18 +56,65 @@ jobs: run: | git config --global core.hooksPath /dev/null + - name: Update PR Branch + uses: actions/github-script@v6 + if: github.event.workflow_run.event == 'pull_request' && steps.download-artifacts.outputs.found_artifact == 'true' + with: + github-token: ${{ secrets.MUTATION_TOKEN }} + script: | + // use API to get the PR data since we can't rely on the context across forks + const pulls = await github.rest.pulls.list({ + per_page: 1, + owner: context.repo.owner, + repo: context.repo.repo, + head: `${context.payload.workflow_run.head_repository.full_name}:${context.payload.workflow_run.head_branch}` + }); + + const prContextData = pulls.data[0]; + const prNumber = prContextData.number; + const originalSha = prContextData.head.sha; + + try { + console.log("Updating PR branch"); + await github.rest.pulls.updateBranch({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + console.log("PR branch updated"); + + let updatedSha = originalSha; + let retries = 0; + const MAX_RETRIES = 10; + while (updatedSha == originalSha && retries++ < MAX_RETRIES) { + console.log(`Waiting for PR branch to update (attempt ${retries}/${MAX_RETRIES})`); + + await new Promise(r => setTimeout(r, 500)); + const updatedPR = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + updatedSha = updatedPR.data.head.sha; + } + } catch (error) { + // The branch is already up to date or can't otherwise be updated + // That's fine, we tried our best + console.warn(error); + } + - name: Checkout Workflow Branch if: steps.download-artifacts.outputs.found_artifact == 'true' uses: actions/checkout@v3 with: - token: ${{secrets.MUTATION_TOKEN}} + token: ${{ secrets.MUTATION_TOKEN }} ref: ${{ github.event.workflow_run.head_branch }} repository: ${{ github.event.workflow_run.head_repository.full_name }} path: repo - id: self_mutation if: steps.download-artifacts.outputs.found_artifact == 'true' - name: Apply downloaded pathes + name: Apply downloaded patches working-directory: repo env: HEAD_REF: ${{ github.event.workflow_run.head_branch }} @@ -76,29 +122,12 @@ jobs: git config user.name "monada-bot[bot]" git config user.email "monabot@monada.co" - # if ../patches/update.diff/update.diff exists, apply it first - UPDATE_PATCH_FILE="../patches/update.diff/update.diff" - if [ -f $UPDATE_PATCH_FILE ]; then - echo "Updating branch" - git apply --binary $UPDATE_PATCH_FILE - if [ $? -eq 0 ]; then - git add --all - git commit -s -m "Merge branch 'main' into $HEAD_REF" - echo "Patch applied successfully" - rm $UPDATE_PATCH_FILE - else - echo "Patch failed to apply" - cat $UPDATE_PATCH_FILE - exit 1 - fi - fi - for f in $(find ../patches/*.diff/*.diff); do echo "Applying $f" git apply --binary $f if [ $? -eq 0 ]; then git add --all - git commit -s -m "chore: self mutation ($f)" + git commit -s -m "chore: self mutation ($(basename $f))" echo "Patch applied successfully" rm $f else @@ -116,12 +145,13 @@ jobs: with: github-token: ${{ secrets.MUTATION_TOKEN }} script: | + // use API to get the PR number since we can't rely on the context across forks const pulls = await github.rest.pulls.list({ + per_page: 1, owner: context.repo.owner, repo: context.repo.repo, head: `${context.payload.workflow_run.head_repository.full_name}:${context.payload.workflow_run.head_branch}` }); - const prNumber = pulls.data[0].number; const labels = ["⚠️ pr/review-mutation"]; diff --git a/apps/wing-console/console/ui/src/layout/layout-provider.tsx b/apps/wing-console/console/ui/src/layout/layout-provider.tsx index 81b9e8e06bf..8af50a15386 100644 --- a/apps/wing-console/console/ui/src/layout/layout-provider.tsx +++ b/apps/wing-console/console/ui/src/layout/layout-provider.tsx @@ -39,6 +39,9 @@ export function LayoutProvider({ }, ], }, + statusBar: { + showThemeToggle: false, + }, panels: { rounded: false, }, diff --git a/examples/tests/valid/struct_from_json.w b/examples/tests/valid/struct_from_json.w index d3db2b0b4a9..454b5ea9cdb 100644 --- a/examples/tests/valid/struct_from_json.w +++ b/examples/tests/valid/struct_from_json.w @@ -236,4 +236,16 @@ test "lifting a student" { assert(studentInflight1.dob.month == 10); assert(studentInflight1.dob.day == 10); assert(studentInflight1.dob.year == 2005); -} \ No newline at end of file +} + +// bring structs from other files +bring "./subdir/structs.w" as externalStructs; + +let jj1 = { + data: { + val: 10 + } +}; + +let externalBar = externalStructs.MyOtherStruct.fromJson(jj1); +assert(externalBar.data.val == 10); \ No newline at end of file diff --git a/examples/tests/valid/subdir/structs.w b/examples/tests/valid/subdir/structs.w new file mode 100644 index 00000000000..5307cdca1d0 --- /dev/null +++ b/examples/tests/valid/subdir/structs.w @@ -0,0 +1,7 @@ +struct MyStruct { + val: num; +} + +struct MyOtherStruct { + data: MyStruct; +} \ No newline at end of file diff --git a/libs/wingc/src/jsify.rs b/libs/wingc/src/jsify.rs index 467af6b68b4..05ab515c4ff 100644 --- a/libs/wingc/src/jsify.rs +++ b/libs/wingc/src/jsify.rs @@ -540,8 +540,9 @@ impl<'a> JSifier<'a> { ExprKind::Reference(Reference::InstanceMember { object, .. }) => { self.jsify_expression(&object, ctx) }, - ExprKind::Reference(Reference::TypeMember { .. }) => { - expr_string.clone().split(".").next().unwrap_or("").to_string() + ExprKind::Reference(Reference::TypeMember { property, .. }) => { + // remove the property name from the expression string + expr_string.split(".").filter(|s| s != &property.name).join(".") }, _ => expr_string, } @@ -1634,8 +1635,9 @@ fn get_public_symbols(scope: &Scope) -> Vec { } // interfaces are bringable, but there's nothing to emit StmtKind::Interface(_) => {} - // structs are bringable, but there's nothing to emit - StmtKind::Struct { .. } => {} + StmtKind::Struct { name, .. } => { + symbols.push(name.clone()); + } StmtKind::Enum { name, .. } => { symbols.push(name.clone()); } diff --git a/libs/wingc/src/type_check.rs b/libs/wingc/src/type_check.rs index 6f21ffccf94..3108b103ee0 100644 --- a/libs/wingc/src/type_check.rs +++ b/libs/wingc/src/type_check.rs @@ -4609,8 +4609,6 @@ impl<'a> TypeChecker<'a> { } } } - let lookup = env.lookup(&s.name, None); - let type_ = lookup.unwrap().as_type().unwrap(); let new_class = self.hydrate_class_type_arguments(env, WINGSDK_STRUCT, vec![type_]); let v = self.get_property_from_class_like(new_class.as_class().unwrap(), property, true); diff --git a/libs/wingcompiler/package.json b/libs/wingcompiler/package.json index 469da4c9287..49c9cd2cc4c 100644 --- a/libs/wingcompiler/package.json +++ b/libs/wingcompiler/package.json @@ -17,7 +17,7 @@ "url": "git+https://github.com/winglang/wing.git" }, "scripts": { - "compile:copy-wingc-wasm": "cp ../../target/wasm32-wasi/release/wingc.wasm . && cp wingc.wasm ../../dist/wingc.wasm", + "compile:copy-wingc-wasm": "cp ../../target/wasm32-wasi/release/wingc.wasm . && mkdir -p ../../dist && cp wingc.wasm ../../dist/wingc.wasm", "compile": "tsup-node", "watch": "tsup-node --watch", "test": "vitest run --passWithNoTests", diff --git a/tools/hangar/__snapshots__/test_corpus/valid/bring_local.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/bring_local.w_compile_tf-aws.md index b5f20f9effe..8d26bdf7eba 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/bring_local.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/bring_local.w_compile_tf-aws.md @@ -586,7 +586,7 @@ module.exports = function({ $stdlib }) { })({}) ; const Point = require("./Point.Struct.js")($stdlib.std.Struct, $stdlib.core.NodeJsCode.fromInline); - return { Util, Store, Color }; + return { Util, Store, Color, Point }; }; ``` diff --git a/tools/hangar/__snapshots__/test_corpus/valid/struct_from_json.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/struct_from_json.w_compile_tf-aws.md index 401aecf5a4f..7d1a8de6c20 100644 --- a/tools/hangar/__snapshots__/test_corpus/valid/struct_from_json.w_compile_tf-aws.md +++ b/tools/hangar/__snapshots__/test_corpus/valid/struct_from_json.w_compile_tf-aws.md @@ -227,6 +227,67 @@ module.exports = function(stdStruct, fromInline) { ``` +## MyOtherStruct.Struct.js +```js +module.exports = function(stdStruct, fromInline) { + class MyOtherStruct { + static jsonSchema() { + return { + id: "/MyOtherStruct", + type: "object", + properties: { + data: { "$ref": "#/$defs/MyStruct" }, + }, + required: [ + "data", + ], + $defs: { + "MyStruct": { type: "object", "properties": require("./MyStruct.Struct.js")().jsonSchema().properties }, + } + } + } + static fromJson(obj) { + return stdStruct._validate(obj, this.jsonSchema()) + } + static _toInflightType(context) { + return fromInline(`require("./MyOtherStruct.Struct.js")(${ context._lift(stdStruct) })`); + } + } + return MyOtherStruct; +}; + +``` + +## MyStruct.Struct.js +```js +module.exports = function(stdStruct, fromInline) { + class MyStruct { + static jsonSchema() { + return { + id: "/MyStruct", + type: "object", + properties: { + val: { type: "number" }, + }, + required: [ + "val", + ], + $defs: { + } + } + } + static fromJson(obj) { + return stdStruct._validate(obj, this.jsonSchema()) + } + static _toInflightType(context) { + return fromInline(`require("./MyStruct.Struct.js")(${ context._lift(stdStruct) })`); + } + } + return MyStruct; +}; + +``` + ## Person.Struct.js ```js module.exports = function(stdStruct, fromInline) { @@ -561,6 +622,7 @@ const $stdlib = require('@winglang/sdk'); const $outdir = process.env.WING_SYNTH_DIR ?? "."; const $wing_is_test = process.env.WING_IS_TEST === "true"; const std = $stdlib.std; +const externalStructs = require("./preflight.structs-1.js")({ $stdlib }); class $Root extends $stdlib.std.Resource { constructor(scope, id) { super(scope, id); @@ -731,6 +793,9 @@ class $Root extends $stdlib.std.Resource { } this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:flight school student :)",new $Closure1(this,"$Closure1")); this.node.root.new("@winglang/sdk.std.Test",std.Test,this,"test:lifting a student",new $Closure2(this,"$Closure2")); + const jj1 = ({"data": ({"val": 10})}); + const externalBar = (externalStructs.MyOtherStruct.fromJson(jj1)); + {((cond) => {if (!cond) throw new Error("assertion failed: externalBar.data.val == 10")})((((a,b) => { try { return require('assert').deepStrictEqual(a,b) === undefined; } catch { return false; } })(externalBar.data.val,10)))}; } } const $App = $stdlib.core.App.for(process.env.WING_TARGET); @@ -738,3 +803,14 @@ new $App({ outdir: $outdir, name: "struct_from_json", rootConstruct: $Root, plug ``` +## preflight.structs-1.js +```js +module.exports = function({ $stdlib }) { + const std = $stdlib.std; + const MyStruct = require("./MyStruct.Struct.js")($stdlib.std.Struct, $stdlib.core.NodeJsCode.fromInline); + const MyOtherStruct = require("./MyOtherStruct.Struct.js")($stdlib.std.Struct, $stdlib.core.NodeJsCode.fromInline); + return { MyStruct, MyOtherStruct }; +}; + +``` +