Skip to content
Open
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
a996262
build(typechecking): bundle pinned browser analysis dependencies
Josverl Aug 9, 2026
d7f6dc0
feat(typechecking): add browser analysis service
Josverl Aug 9, 2026
8ff81b6
feat(typechecking): add settings and diagnostics UI
Josverl Aug 9, 2026
9a293ce
feat(typechecking): integrate editor and workspace lifecycle
Josverl Aug 9, 2026
b35c679
feat(typechecking): update worker version to v0.2.5
Josverl Aug 9, 2026
9e5d529
Remove sizeOf method from fs_cache.js
vshymanskyy Aug 10, 2026
b8c9f04
feat(typechecking): Set mpy-cross as diagnostic source.
Josverl Aug 10, 2026
cb9dd00
feat(diagnostics): Rename diagnostics tab to Problems.
Josverl Aug 10, 2026
f943811
feat(typechecking): Include ExceptionClass for mpy-cross diagnostics.
Josverl Aug 10, 2026
74a16d8
refactor: group typechecking modules
Josverl Aug 10, 2026
6622991
fix: quiet typechecking components by default
Josverl Aug 10, 2026
5c4bd40
fix: default typechecking to opened files
Josverl Aug 10, 2026
1fa64fe
fix: trigger dotted completions reliably
Josverl Aug 10, 2026
321dad6
fixup: Formatting tests
Josverl Aug 11, 2026
482c2bd
fix: improve layout and responsiveness of diagnostic items and termin…
Josverl Aug 12, 2026
e331458
refactor: Switch to @mp-codemirror/ packages.
Josverl Aug 16, 2026
65c636b
fix(typechecking): select the MicroPython stdlib stubs
Josverl Aug 17, 2026
20f0dc2
update @mp-codemirror/lsp-client and @mp-codemirror/pyright-worker
Josverl Aug 23, 2026
3f88e7f
Hide install from PyPI outside Advanced mode
Josverl Aug 23, 2026
617ae4c
feat(typechecking): enhance stub package management and localization
Josverl Aug 23, 2026
d583e4b
feat(diagnostics): refactor layout of diagnostic items.
Josverl Aug 23, 2026
7788da0
feat(typechecking): Add autodetect/select for type stubs.
Josverl Aug 24, 2026
e9f0602
feat(deployment): add GitHub Actions workflow for deploying beta to P…
Josverl Aug 31, 2026
46fd914
Add Beta badge
Josverl Aug 31, 2026
b60a9fd
CI: Avoid competing for pages.
Josverl Aug 31, 2026
5775c35
feat(repl): Allow specifying name for BLEUART initialization.
Josverl Aug 31, 2026
0eea434
feat(css): Set max-width for CodeMirror tooltips to prevent resize fe…
Josverl Aug 31, 2026
71bc939
fix(typechecking): finish Problems terminology rename
Josverl Aug 31, 2026
0c28571
deps: update @mp-codemirror/pyright-worker to version ^0.4.3
Josverl Aug 31, 2026
ac660af
deps: Update @micropython/micropython-webassembly-pyscript v1.29.0
Josverl Aug 31, 2026
48fffe1
feat(build): Include build identifier in WASM URL for cache management
Josverl Aug 31, 2026
5b38c1b
feat(local-build): Add support for local CodeMirror packages and enha…
Josverl Aug 31, 2026
522392c
feat: select verified type-checking runtimes
Josverl Aug 31, 2026
a228e8d
fix: await type-checking shutdown
Josverl Aug 31, 2026
676621d
deps: update pyright worker to 0.4.4
Josverl Sep 1, 2026
008558b
fix(mcp): include password in connect_device call for serial connection
Josverl Aug 23, 2026
cd5baf8
fix: make python-minifier MicroPython compatible
Josverl Sep 1, 2026
2b9c2d6
style: reformat build.py
Josverl Sep 1, 2026
edc0c44
fix(typechecking): report ready only after the runtime settles
Josverl Sep 1, 2026
4992329
test: baseline browser test migration
Josverl Sep 1, 2026
64ef123
test: add Playwright browser tooling
Josverl Sep 1, 2026
1b69d2c
test: add shared Playwright helpers
Josverl Sep 1, 2026
322cf24
chore: add results/playwright to .gitignore
Josverl Sep 1, 2026
2d045df
test: convert browser suite to Playwright
Josverl Sep 1, 2026
12ecac7
test: wire browser scripts to Playwright
Josverl Sep 1, 2026
b019b8d
test: retire pytest browser stack
Josverl Sep 1, 2026
a33e7c7
fix(typechecking): allow graceful worker shutdown
Josverl Sep 1, 2026
9be357f
ci: run Playwright browser tests
Josverl Sep 1, 2026
941776e
fix(typechecking): preserve integration after upstream rebase
Josverl Sep 1, 2026
556bb1b
fix(ci): provision browser test toolchain
Josverl Sep 1, 2026
6fda8f8
refactor(typechecking): adopt @mp-typing packages
Josverl Sep 2, 2026
e3e303e
feat(typechecking): integrate Viper tools stubs support
Josverl Sep 2, 2026
975fb0a
feat(typechecking): Add documentation for type-checking integration.
Josverl Sep 2, 2026
d9c1e77
feat(typechecking): preserve editor extensions during runtime restarts
Josverl Sep 2, 2026
c8efd1f
fix(rawmode): improve ID formatting in board information output
Josverl Sep 2, 2026
6e1d14b
feat(docs): update README and Advanced Mode documentation with new ty…
Josverl Sep 2, 2026
bfe62f5
feat: implement reusable input dialog for user prompts
Josverl Sep 3, 2026
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
82 changes: 82 additions & 0 deletions .github/workflows/beta-static.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Deploy a configured beta branch from a fork to GitHub Pages.
name: Deploy beta to Pages

on:
# Branch filtering is handled by deploy-check so
# BETA_BRANCH remains the single place to configure the branch for reuse.
push:
workflow_dispatch:

env:
BETA_REPOSITORY: Josverl/viperide
BETA_BRANCH: feat/type_checking
VIPER_IDE_DEPLOYMENT_TAG: TYPING_BETA

permissions:
contents: read
pages: write
id-token: write

# GitHub Pages has one deployment target per repository. Use the same
# concurrency group as the production workflow so deployments cannot overlap.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
deploy-check:
runs-on: ubuntu-latest
outputs:
should_deploy: ${{ steps.check_source.outputs.should_deploy }}
steps:
- name: Check repository and branch
id: check_source
shell: bash
run: |
if [[ "${GITHUB_REPOSITORY,,}" == "${BETA_REPOSITORY,,}" &&
"$GITHUB_REF_TYPE" == "branch" &&
"$GITHUB_REF_NAME" == "$BETA_BRANCH" ]]; then
echo "should_deploy=true" >> "$GITHUB_OUTPUT"
else
echo "should_deploy=false" >> "$GITHUB_OUTPUT"
fi

deploy-beta:
needs: deploy-check
# The env context is unavailable in a job-level condition, so the validation
# job exposes the comparison as an output.
if: needs.deploy-check.outputs.should_deploy == 'true'
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
env:
# Project Pages is hosted below /<repository-name>. build.py substitutes
# this value into application URLs, the manifest, and generated HTML.
VIPER_IDE_BASE_URL: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}
steps:
- name: Checkout beta branch
uses: actions/checkout@v7

# Match the production workflow: prepare dependencies once, then lint and
# test before creating the deployable build.
- name: Test
run: |
python3 build.py --prepare
npm run lint
npm run test

- name: Build beta site
run: python3 build.py --skip-tests

- name: Setup Pages
uses: actions/configure-pages@v6

- name: Upload beta site
uses: actions/upload-pages-artifact@v5
with:
path: ./build

- name: Deploy beta to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
41 changes: 41 additions & 0 deletions .github/workflows/pr-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,44 @@ jobs:

- name: Build
run: python3 build.py --skip-tests

browser-test:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.55.0-noble
options: --user 1001
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
browser: [chromium]
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Prepare
run: python3 build.py --prepare

- name: Build
run: python3 build.py --skip-tests

- name: Verify Playwright version
run: test "$(npx playwright --version)" = "Version 1.55.0"

- name: Run browser tests
run: npx playwright test --project "${{ matrix.browser }}"

- name: Upload browser test failure artifacts
if: failure()
uses: actions/upload-artifact@v7
with:
name: playwright-${{ matrix.browser }}-${{ github.run_attempt }}
path: |
results/playwright-report/
results/playwright/**/trace.zip
if-no-files-found: ignore
4 changes: 3 additions & 1 deletion .github/workflows/static.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ concurrency:
jobs:
# Single deploy job since we're just deploying
deploy:
# Forks can provide their own Pages workflow without publishing main here.
if: github.repository == 'vshymanskyy/ViperIDE'
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
Expand Down Expand Up @@ -53,7 +55,7 @@ jobs:
uses: actions/upload-pages-artifact@v5
with:
# Upload entire repository
path: './build'
path: "./build"

- name: Deploy to GitHub Pages
id: deployment
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ node_modules/
extra/
src/tools_vfs/lib/python_minifier/
bun.lock
results/playwright
122 changes: 93 additions & 29 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,90 +12,130 @@
if not BASE_URL:
BASE_URL = os.environ["VIPER_IDE_BASE_URL"] = "http://localhost:10001"


def run(cmd):
subprocess.run(cmd, shell=isinstance(cmd, str), check=True)


def readfile(fn):
with open(fn, 'r', encoding='utf-8') as f:
with open(fn, "r", encoding="utf-8") as f:
return f.read()


def remove_files(*filenames):
for fn in filenames:
try:
remove(fn)
except FileNotFoundError:
pass


def gen_translations(src, dst):
result = {}
for fn in glob.glob('*.json', root_dir=src):
lang = fn.replace('.json', '')
for fn in glob.glob("*.json", root_dir=src):
lang = fn.replace(".json", "")
result[lang] = json.loads(readfile(path.join(src, fn)))
with open(dst, 'w', encoding='utf-8') as f:
json.dump(result, f, separators=(',',':'), ensure_ascii=False, sort_keys=True)
with open(dst, "w", encoding="utf-8") as f:
json.dump(result, f, separators=(",", ":"), ensure_ascii=False, sort_keys=True)


def gen_manifest(src, dst):
pkg = json.loads(readfile('package.json'))
pkg = json.loads(readfile("package.json"))
result = json.loads(readfile(src))
result['version'] = pkg['version']
with open(dst, 'w', encoding='utf-8') as f:
json.dump(result, f, separators=(',',':'), ensure_ascii=False)
result["version"] = pkg["version"]
with open(dst, "w", encoding="utf-8") as f:
json.dump(result, f, separators=(",", ":"), ensure_ascii=False)


def gen_tar(src, dst):
def reset_tarinfo(tarinfo):
# Stray bytecode caches must never reach the device image. Returning
# None drops the entry, and for a directory also stops the recursion.
if '__pycache__' in tarinfo.name.split('/') or tarinfo.name.endswith('.pyc'):
if "__pycache__" in tarinfo.name.split("/") or tarinfo.name.endswith(".pyc"):
return None
tarinfo.uid = 0
tarinfo.gid = 0
tarinfo.uname = ""
tarinfo.gname = ""
tarinfo.mtime = 0
return tarinfo
with open(dst, 'wb') as raw:
with gzip.GzipFile(filename='', mode='wb', fileobj=raw, mtime=0) as gz:
with tarfile.open(fileobj=gz, mode='w') as tar:

with open(dst, "wb") as raw:
with gzip.GzipFile(filename="", mode="wb", fileobj=raw, mtime=0) as gz:
with tarfile.open(fileobj=gz, mode="w") as tar:
for item in sorted(os.listdir(src)):
item_path = os.path.join(src, item)
tar.add(item_path, arcname=item, filter=reset_tarinfo)


def vendor_pypi_package(spec, dest):
# --upgrade is required: without it pip silently skips an existing target
# directory, so a stale vendored copy would never be replaced.
run([sys.executable, "-m", "pip", "install", "--target", dest,
"--no-compile", "--no-deps", "--upgrade", "--quiet", spec])
run(
[
sys.executable,
"-m",
"pip",
"install",
"--target",
dest,
"--no-compile",
"--no-deps",
"--upgrade",
"--quiet",
spec,
]
)
# pip also drops console scripts and metadata into the target; neither
# belongs in the on-device filesystem image.
rmtree(path.join(dest, "bin"), ignore_errors=True)
for meta in glob.glob("*.dist-info", root_dir=dest):
rmtree(path.join(dest, meta), ignore_errors=True)


def combine(dst):
# Insert CSS and JS into HTML
combined = readfile(dst).replace(
'<link rel="stylesheet" href="./app.css">', '<style>\n' + readfile('build/app.css') + '\n</style>'
).replace(
'<link rel="stylesheet" href="./viper_lib.css">', '<style>\n' + readfile('build/viper_lib.css') + '\n</style>'
).replace(
'<script src="./app.js"></script>', '<script>\n' + readfile('build/app.js') + '\n</script>'
).replace(
'<script src="./viper_lib.js"></script>', '<script>\n' + readfile('build/viper_lib.js') + '\n</script>'
combined = (
readfile(dst)
.replace(
'<link rel="stylesheet" href="./app.css">',
"<style>\n" + readfile("build/app.css") + "\n</style>",
)
.replace(
'<link rel="stylesheet" href="./viper_lib.css">',
"<style>\n" + readfile("build/viper_lib.css") + "\n</style>",
)
.replace(
'<script src="./app.js"></script>',
"<script>\n" + readfile("build/app.js") + "\n</script>",
)
.replace(
'<script src="./viper_lib.js"></script>',
"<script>\n" + readfile("build/viper_lib.js") + "\n</script>",
)
)

for asset in ("app.css", "viper_lib.css", "app.js", "viper_lib.js"):
if f'{BASE_URL}/{asset}"' in combined:
raise Exception(f"{dst}: failed to inline {asset}")

# Write the combined content
with open(dst, 'w', encoding='utf-8') as f:
with open(dst, "w", encoding="utf-8") as f:
f.write(combined)


if __name__ == "__main__":
import argparse

parser = argparse.ArgumentParser(description="Build the VIPER IDE")
parser.add_argument("--skip-tests", action="store_true", help="Skip linting and tests")
parser.add_argument("--prepare", action="store_true", help="Only vendor dependencies (for running tests without a full build)")
parser.add_argument(
"--skip-tests", action="store_true", help="Skip linting and tests"
)
parser.add_argument(
"--prepare",
action="store_true",
help="Only vendor dependencies (for running tests without a full build)",
)
args = parser.parse_args()

# Prepare
Expand All @@ -106,7 +146,25 @@ def combine(dst):
gen_translations("./src/lang/", "build/translations.json")
gen_manifest("./src/manifest.json", "build/manifest.json")

rmtree("src/tools_vfs/lib/python_minifier", ignore_errors=True)
vendor_pypi_package("python-minifier==3.2.0", "src/tools_vfs/lib")
# CPython permits starred arguments after keywords, but MicroPython does
# not. Keep this narrow compatibility rewrite until upstream supports it.
ast_compat = "src/tools_vfs/lib/python_minifier/ast_compat.py"
source = readfile(ast_compat)
replacements = {
"Constant(value=s, *args, **kwargs)": "Constant(*args, value=s, **kwargs)",
"Constant(value=n, *args, **kwargs)": "Constant(*args, value=n, **kwargs)",
"Constant(value=literal_eval('...'), *args, **kwargs)": "Constant(*args, value=literal_eval('...'), **kwargs)",
}
for old, new in replacements.items():
if old not in source:
raise RuntimeError(
f"Expected python-minifier compatibility pattern missing: {old}"
)
source = source.replace(old, new)
with open(ast_compat, "w", encoding="utf-8") as f:
f.write(source)
gen_tar("src/tools_vfs", "build/assets/tools_vfs.tar.gz")
gen_tar("src/vm_vfs", "build/assets/vm_vfs.tar.gz")

Expand All @@ -130,18 +188,24 @@ def combine(dst):
combine("build/benchmark.html")

# Cleanup
#remove_files("build/translations.json")
# remove_files("build/translations.json")
remove_files("build/app.css", "build/viper_lib.css")
remove_files("build/app.js", "build/viper_lib.js")

# Add assets from packages
cp("node_modules/@micropython/micropython-webassembly-pyscript/micropython.wasm", "./build/assets/micropython.wasm")
cp(
"node_modules/@micropython/micropython-webassembly-pyscript/micropython.wasm",
"./build/assets/micropython.wasm",
)
# mpy-cross ships one binary per .mpy ABI; python_utils.js picks the one the
# connected board can import, so all of them have to be served.
mpy_cross = "node_modules/@vshymanskyy/mpy-cross-wasm/build"
for wasm in sorted(glob.glob("mpy-cross-v*.wasm", root_dir=mpy_cross)):
cp(path.join(mpy_cross, wasm), f"./build/assets/{wasm}")
cp("node_modules/@astral-sh/ruff-wasm-web/ruff_wasm_bg.wasm", "./build/assets/ruff_wasm_bg.wasm")
cp(
"node_modules/@astral-sh/ruff-wasm-web/ruff_wasm_bg.wasm",
"./build/assets/ruff_wasm_bg.wasm",
)

print()
print("Build complete.")
Loading