-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat(apps): add +init-template and +deploy shortcuts #2571
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Allen-D2026
wants to merge
51
commits into
main
Choose a base branch
from
feat/apps-app-dev-shortcuts
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 48 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
25e034e
feat(apps): add +app-dev-init-app shortcut
Allen-D2026 0bb08e7
refactor(apps): extract parsePreReleaseKVs helper
Allen-D2026 d52cdaf
feat(apps): add +app-dev-publish shortcut
Allen-D2026 45897b1
feat(apps): register app-dev shortcuts
Allen-D2026 6d29225
docs(apps): add app-dev shortcut skill references
Allen-D2026 8f91de8
chore(apps): exempt presigned TOS upload from raw-http lint
Allen-D2026 9875a9e
fix(apps): gate dry-run on sensitive files and fix misleading error copy
Allen-D2026 eb7f0da
refactor(apps): rename +app-dev-init-app to +app-dev-init-template
Allen-D2026 8cd9006
feat(apps): render init-template natively from npm registry
Allen-D2026 b5ad05a
feat(apps): fall back to the official npm registry and harden extract…
Allen-D2026 474aeae
docs(apps): note registry fallback in init-template reference
Allen-D2026 4fda360
feat(apps): support explicit --template for init-template
Allen-D2026 89ed8f6
feat(apps): add optional --app-id to publish and drop archType
Allen-D2026 9eed68b
feat(apps): adopt miaoda.json artifact-hosting protocol
Allen-D2026 92e2566
feat(apps): enforce protocol output layout and routes.json schema
Allen-D2026 95c0035
docs(apps): align references with miaoda.json protocol
Allen-D2026 ba9239d
feat(apps): scaffold in place by default for init-template
Allen-D2026 92d8235
fix(apps): resolve in-place project name without filepath.Abs
Allen-D2026 c90c5ba
fix(apps): emit raw JSON so next_steps keep literal characters
Allen-D2026 a8eabdf
feat(apps): adopt the artifact-hosting pre_release contract
Allen-D2026 af7afe3
feat(apps): ignore non-protocol build artifacts instead of rejecting
Allen-D2026 c801508
docs(apps): note non-protocol artifacts are skipped on publish
Allen-D2026 99957ad
fix(apps): validate routes.json as a route enumeration array
Allen-D2026 032ffde
feat(apps): rename pre_release upload key to artifact_url
Allen-D2026 ef067f5
feat(apps): support pinning template version for init-template
Allen-D2026 0e97aa1
feat(apps): align +app-dev-publish with the hosting protocol
Allen-D2026 2e43d32
feat(apps): add --registry escape hatch and html type to +app-dev-ini…
Allen-D2026 f5fa9e0
feat(apps): wait briefly for async releases in +app-dev-publish
Allen-D2026 84fa851
fix(apps): satisfy nilerr/forbidigo in release await path
Allen-D2026 6c421fd
refactor(apps): rename +app-dev-init-template/+app-dev-publish to +in…
Allen-D2026 1929edc
feat(apps): rename the project declaration file to spark.json
Allen-D2026 24850a9
fix(apps): recover online_url when a release reports finished without it
Allen-D2026 0200690
feat(apps): return immediately after release acceptance in +deploy
Allen-D2026 b275d0e
refactor(apps): align local-dev helper filenames with the apps_ conve…
Allen-D2026 d7a97c0
feat(apps): sync app.url into spark.json when +release-get observes a…
Allen-D2026 42544f3
feat(apps): rename the spark.json state field app.url to app.online_url
Allen-D2026 9903285
feat(apps): enforce declaration MUSTs at the +deploy hosting entry
Allen-D2026 a818db5
polish(apps): clarify stack charset error; pin the declaration gate o…
Allen-D2026 68d0099
feat(apps): enforce the local self-description endpoint at deploy time
Allen-D2026 1d9fa88
feat(apps): relax +deploy gates; add --no-verify
Allen-D2026 f14137b
feat(apps): --no-verify also waives the dev.port declaration requirement
Allen-D2026 102ffc5
fix(apps): compare the endpoint identity against the resolved deploy …
Allen-D2026 3ac01a9
docs(apps): align the identity-check wording with the resolved-target…
Allen-D2026 e53240d
fix(apps): probe the self-description endpoint via localhost for dual…
Allen-D2026 b1146d3
polish(apps): keep routes.json guidance self-contained in comments an…
Allen-D2026 93ce9b4
polish(apps): tidy protocol references in comments and hints
Allen-D2026 700b118
polish(apps): drop a stray section reference from a test comment
Allen-D2026 1a876bb
test(apps): cover error paths and swap test hostnames to RFC 2606 names
Allen-D2026 f4d41bb
fix(apps): route the new local-dev commands in SKILL.md; polish deplo…
Allen-D2026 48af737
docs(apps): withdraw the local-dev routing row from the domain skill
Allen-D2026 bacd725
fix(apps): address review comments on the local-dev chain
Allen-D2026 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| // Copyright (c) 2026 Lark Technologies Pte. Ltd. | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| package apps | ||
|
|
||
| import ( | ||
| "archive/zip" | ||
| "bytes" | ||
| "io" | ||
|
|
||
| "github.com/larksuite/cli/extension/fileio" | ||
| ) | ||
|
|
||
| // appDevZipball is an in-memory zip payload ready for TOS upload. | ||
| type appDevZipball struct { | ||
| Body []byte | ||
| Size int64 | ||
| FileCount int | ||
| } | ||
|
|
||
| // appDevPackEntry is one file of the normalized upload payload. ZipPath is | ||
| // the fixed protocol layout inside the zip (output/... for same-origin | ||
| // artifacts, output_resource/... for CDN artifacts) regardless of the | ||
| // project's directory names. Data comes from AbsPath, or from Content for | ||
| // CLI-generated files (a buildless routes.json). | ||
| type appDevPackEntry struct { | ||
| ZipPath string | ||
| AbsPath string | ||
| Content []byte | ||
| Size int64 | ||
| } | ||
|
|
||
| // buildAppDevZip packs the normalized entries into an in-memory zip: entry | ||
| // names are the fixed output/... and output_resource/... layout the hosting | ||
| // pipeline expects. | ||
| func buildAppDevZip(fio fileio.FileIO, entries []appDevPackEntry) (*appDevZipball, error) { | ||
| var buf bytes.Buffer | ||
| zw := zip.NewWriter(&buf) | ||
| for _, e := range entries { | ||
| w, err := zw.Create(e.ZipPath) | ||
| if err != nil { | ||
| return nil, appsFileIOError(err, "zip create %s failed: %v", e.ZipPath, err) | ||
| } | ||
| if e.AbsPath == "" { | ||
| if _, err := w.Write(e.Content); err != nil { | ||
| return nil, appsFileIOError(err, "zip write %s failed: %v", e.ZipPath, err) | ||
| } | ||
| continue | ||
| } | ||
| f, err := fio.Open(e.AbsPath) | ||
| if err != nil { | ||
| return nil, appsInputPathEntryError(e.AbsPath, err) | ||
| } | ||
| _, err = io.Copy(w, f) | ||
| f.Close() | ||
| if err != nil { | ||
| return nil, appsFileIOError(err, "zip write %s failed: %v", e.ZipPath, err) | ||
| } | ||
| } | ||
| if err := zw.Close(); err != nil { | ||
| return nil, appsFileIOError(err, "zip finalize failed: %v", err) | ||
| } | ||
| size := int64(buf.Len()) | ||
| return &appDevZipball{Body: buf.Bytes(), Size: size, FileCount: len(entries)}, nil | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| // Copyright (c) 2026 Lark Technologies Pte. Ltd. | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| package apps | ||
|
|
||
| import ( | ||
| "archive/zip" | ||
| "bytes" | ||
| "testing" | ||
| ) | ||
|
|
||
| // zipEntryNames opens an in-memory zip and returns its entry names. | ||
| func zipEntryNames(t *testing.T, body []byte) []string { | ||
| t.Helper() | ||
| zr, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) | ||
| if err != nil { | ||
| t.Fatalf("open zip: %v", err) | ||
| } | ||
| names := make([]string, 0, len(zr.File)) | ||
| for _, f := range zr.File { | ||
| names = append(names, f.Name) | ||
| } | ||
| return names | ||
| } | ||
|
|
||
| func TestBuildAppDevZip_MissingSourceFile(t *testing.T) { | ||
| _, err := buildAppDevZip(permissiveFIO{}, []appDevPackEntry{ | ||
| {ZipPath: "output/gone.html", AbsPath: "/nonexistent/gone.html", Size: 1}, | ||
| }) | ||
| if err == nil { | ||
| t.Fatal("an entry whose source file vanished must fail the pack") | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Assert typed errors and preserved causes across these failure-path tests.
These assertions only check error presence or rendered text, so regressions that remove typed metadata or wrapped causes will pass. Use
errors.Asfor the expected typed error and verify the underlying cause witherrors.Isor an equivalent cause assertion. Apply this to the missing-source case here, the spark.json parsing/file-I/O cases inshortcuts/apps/apps_spark_config_test.go, and the HTTP and size-limit failures inshortcuts/apps/apps_init_template_test.go.📍 Affects 3 files
shortcuts/apps/apps_deploy_zip_test.go#L30-L32(this comment)shortcuts/apps/apps_spark_config_test.go#L99-L99shortcuts/apps/apps_init_template_test.go#L813-L823🤖 Prompt for AI Agents
Source: Coding guidelines