Fix web folder paths and allow main-only web hotfix deployment

This commit is contained in:
Hakan Seven committed 2026-09-13 20:30:12 +03:00
1 parent a43fb253f6
commit 95bad2a3b2
5 files changed
+69 -11

No files matched your search

+14
View File
@@ -13,6 +13,11 @@ on:
required: true required: true
type: string type: string
workflow_dispatch: workflow_dispatch:
inputs:
build_main:
description: Build a web hotfix from main without changing the release tag
type: boolean
default: false
permissions: permissions:
contents: read contents: read
@@ -38,6 +43,7 @@ jobs:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag || github.event.release.tag_name }} RELEASE_TAG: ${{ inputs.tag || github.event.release.tag_name }}
RELEASE_COMMIT: ${{ inputs.commit }} RELEASE_COMMIT: ${{ inputs.commit }}
BUILD_MAIN: ${{ github.event_name == 'workflow_dispatch' && inputs.build_main }}
run: | run: |
if [ -z "$RELEASE_TAG" ]; then if [ -z "$RELEASE_TAG" ]; then
RELEASE_TAG=$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName --jq .tagName) RELEASE_TAG=$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName --jq .tagName)
@@ -47,6 +53,13 @@ jobs:
echo "::error::Release tag and commit do not match" echo "::error::Release tag and commit do not match"
exit 1 exit 1
fi fi
if [ "$BUILD_MAIN" = "true" ]; then
if [ "$GITHUB_REF" != "refs/heads/main" ]; then
echo "::error::Web hotfixes must run on main"
exit 1
fi
COMMIT="$GITHUB_SHA"
fi
echo "tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT" echo "tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
echo "commit=$COMMIT" >> "$GITHUB_OUTPUT" echo "commit=$COMMIT" >> "$GITHUB_OUTPUT"
@@ -61,6 +74,7 @@ jobs:
env: env:
RELEASE_TAG: ${{ steps.source.outputs.tag }} RELEASE_TAG: ${{ steps.source.outputs.tag }}
run: | run: |
git merge-base --is-ancestor "$RELEASE_TAG" HEAD
if [ -f scripts/release.py ]; then if [ -f scripts/release.py ]; then
python3 scripts/release.py versions --tag "$RELEASE_TAG" python3 scripts/release.py versions --tag "$RELEASE_TAG"
else else
+4 -1
View File
@@ -31,4 +31,7 @@ To preview release notes, manually run **Weekly release** on `main` with
**publish** unchecked. Check **publish** to release immediately. Rerunning **publish** unchecked. Check **publish** to release immediately. Rerunning
within the same week reuses the original tag and commit. To rebuild just a within the same week reuses the original tag and commit. To rebuild just a
failed target, use GitHub's **Re-run failed jobs**. Manual web deployment failed target, use GitHub's **Re-run failed jobs**. Manual web deployment
always uses the latest published release rather than unreleased `main`. defaults to the latest published release. For a web hotfix, run **Deploy web**
on `main` with **build_main** checked. The commit must descend from that release
and keep its package version; `/app/release.json` records the actual build commit
without moving the release tag.
+31
View File
@@ -8,6 +8,7 @@ import os
from pathlib import Path from pathlib import Path
import subprocess import subprocess
import tempfile import tempfile
import textwrap
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
@@ -15,6 +16,36 @@ import release
class ReleaseTests(unittest.TestCase): class ReleaseTests(unittest.TestCase):
def test_web_source_selection(self):
workflow = (Path(__file__).resolve().parents[1] / ".github/workflows/pages.yml").read_text()
source = workflow.split(" - name: Resolve release source\n", 1)[1]
script = textwrap.dedent(source.split(" run: |\n", 1)[1].split("\n - uses:", 1)[0])
mock_gh = 'gh() { if [ "$1" = release ]; then echo v2026.37; else echo release-sha; fi; }\n'
cases = (
({}, "release-sha", None),
({"RELEASE_TAG": "v2026.37", "RELEASE_COMMIT": "release-sha"}, "release-sha", None),
({"RELEASE_COMMIT": "wrong-sha"}, None, "Release tag and commit do not match"),
({"BUILD_MAIN": "true"}, "main-sha", None),
({"BUILD_MAIN": "true", "GITHUB_REF": "refs/heads/other"}, None, "Web hotfixes must run on main"),
)
with tempfile.TemporaryDirectory() as temp:
output = Path(temp) / "output"
for overrides, commit, error in cases:
with self.subTest(overrides=overrides):
output.write_text("")
result = subprocess.run(["bash", "-e", "-c", mock_gh + script], text=True, capture_output=True, env={
**os.environ, "GITHUB_REPOSITORY": "owner/repo", "GITHUB_REF": "refs/heads/main",
"GITHUB_SHA": "main-sha", "GITHUB_OUTPUT": str(output),
"RELEASE_TAG": "", "RELEASE_COMMIT": "", "BUILD_MAIN": "false", **overrides,
})
if error:
self.assertNotEqual(result.returncode, 0)
self.assertIn(error, result.stdout)
self.assertEqual(output.read_text(), "")
else:
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(output.read_text(), f"tag=v2026.37\ncommit={commit}\n")
def test_build_metadata(self): def test_build_metadata(self):
with tempfile.TemporaryDirectory() as temp: with tempfile.TemporaryDirectory() as temp:
binary = str(Path(temp) / "build-script") binary = str(Path(temp) / "build-script")
+1
View File
@@ -18,3 +18,4 @@ The drawing screenshot also supplies the social preview; website image URLs chan
GitHub Pages runs on release publication, a call from the weekly release workflow, GitHub Pages runs on release publication, a call from the weekly release workflow,
or a manual run. Commits and pushes do not start workflows. Deployments use website or a manual run. Commits and pushes do not start workflows. Deployments use website
files from `main` and build the app from the release, recording both sources separately. files from `main` and build the app from the release, recording both sources separately.
Manual runs can select **build_main** to publish a web hotfix; see `docs/releases.md`.
+19 -10
View File
@@ -249,16 +249,25 @@ impl OpenCADStudio {
.map_or(1, |h| h.show_solid_history), .map_or(1, |h| h.show_solid_history),
} }
}, },
crate::ui::window::options::Folders { {
config: crate::config::config_dir() #[cfg(not(target_arch = "wasm32"))]
.map(|p| p.display().to_string()), {
plot_styles: crate::io::plot_style::plot_styles_dir() crate::ui::window::options::Folders {
.ok() config: crate::config::config_dir()
.map(|p| p.display().to_string()), .map(|p| p.display().to_string()),
plugins: crate::plugin::external::plugins_dir() plot_styles: crate::io::plot_style::plot_styles_dir()
.map(|p| p.display().to_string()), .ok()
autosave: crate::config::config_dir() .map(|p| p.display().to_string()),
.map(|_| std::env::temp_dir().display().to_string()), plugins: crate::plugin::external::plugins_dir()
.map(|p| p.display().to_string()),
autosave: crate::config::config_dir()
.map(|_| std::env::temp_dir().display().to_string()),
}
}
#[cfg(target_arch = "wasm32")]
{
crate::ui::window::options::Folders::default()
}
}, },
self.double_click_block_refedit, self.double_click_block_refedit,
self.double_click_block_attedit, self.double_click_block_attedit,