feat(appscript): add push, pull, deploy, and version management - #1005
feat(appscript): add push, pull, deploy, and version management#1005haosdent wants to merge 1 commit into
Conversation
`gog appscript` could read a project but never change one: there was no way
to push local sources, cut a version, or manage web app deployments.
Add six commands:
appscript push <scriptId> <dir> replace project content from a directory
appscript pull <scriptId> <dir> write project content into a directory
appscript deploy <scriptId> cut a version and deploy it
appscript deployments <scriptId> list deployments
appscript undeploy <scriptId> <id> delete a deployment
appscript versions <scriptId> list versions
`deploy --deployment-id` updates an existing deployment in place, so a
redeploy keeps the same web app URL.
Apps Script stores a file's extension in its type rather than its name, so
push and pull translate between `Code.gs` and `{name: "Code", type:
SERVER_JS}`. A manifest is mandatory -- UpdateContent rejects a payload
without appsscript.json -- so push fails early with a clear message rather
than surfacing an opaque API error.
Both list commands go through the shared pagination helpers, so `--max`,
`--page`, `--all`, and `--fail-empty` behave as they do elsewhere; the API
defaults to 50 items per page and `deploy` cuts a version every time, so
dropping the next page token would silently under-report versions. Deleting
a deployment permanently breaks its web app URL, so `undeploy` goes through
`dryRunAndConfirmDestructive`.
The new leaves are also added to the bundled safety profiles: those are
allow-lists that fail closed, so without an entry even the read-only `pull`,
`deployments`, and `versions` would be rejected in a baked build.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
🦞👀 Pull request received. I will update this pull request when review starts. |
|
Codex review: needs real behavior proof before merge. Reviewed August 19, 2026, 9:29 PM ET / August 20, 2026, 01:29 UTC. ClawSweeper reviewWhat this changesThe PR adds CLI commands to push and pull Apps Script project files, create and manage deployments, and list project versions. Merge readiness⛔ Blocked until real behavior proof is added - 11 items remain Keep open: current main lacks these Apps Script commands, but the proposed push path can follow local symlinks and upload data outside the selected project directory. The new permanent command surface and destructive-push policy also need maintainer direction, and live Google API proof is missing. Priority: P1 Review scores
Verification
How this fits togetherThe Apps Script command group converts local project files and CLI flags into Google Apps Script API requests, then reports project, deployment, or version data. This PR adds filesystem synchronization and deployment lifecycle operations to that existing API client. flowchart LR
A[Local project directory] --> B[Apps Script CLI commands]
C[CLI flags and safety profiles] --> B
B --> D[Input validation]
D --> E[Google Apps Script API]
E --> F[Project content and deployments]
E --> G[CLI output]
E --> H[Pulled local files]
Decision needed
Why: This is a large new API surface with an intentional destructive behavior; repository vision requires maintainer direction rather than inferring a permanent CLI contract from the implementation alone. Before merge
Findings
Agent review detailsSecurityNeeds attention: The new upload path follows symlinks and can disclose an external local file to the configured Apps Script project. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Reject symlinked push inputs, add regression coverage and redacted live Apps Script proof, then land only after maintainers choose the destructive-push and command-hierarchy policy. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible: place a symlink named Is this the best way to solve the issue? No: the feature fits an existing area, but it needs a symlink boundary fix, live provider proof, and maintainer confirmation of the permanent destructive-push and command-shape contract. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against eb85a99366eb. LabelsLabel changes:
Label justifications:
EvidenceSecurity concerns:
What I checked:
Likely related people:
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
|
Scope
gog appscriptcould read a project (get,content) and run a deployedfunction, but never change one. This adds the write half so an Apps Script
project can be developed and released from the CLI:
appscript push <scriptId> <dir>appscript pull <scriptId> <dir>appscript deploy <scriptId>appscript deployments <scriptId>appscript undeploy <scriptId> <deploymentId>appscript versions <scriptId>Notes on the design
deploy --deployment-idupdates in place, so a redeploy keeps the sameweb app URL. Without it, every deploy mints a new URL, which is the main
thing that makes scripted redeploys painful today.
type,not its
name, so push/pull translate betweenCode.gsand{name: "Code", type: SERVER_JS}. Onlyappsscript.jsonis accepted asJSON, so a stray
package-lock.jsonin the directory is skipped withoutbeing read.
UpdateContentrejects a payload withoutappsscript.json; push checks up front and says so, instead of passingthrough an opaque API error.
loadPagedItems/writePagedJSONResult/printNextPageHintWithAll, so--max,--page,--all, and--fail-emptybehave as they do elsewhere. This matters here:the API defaults to 50 per page and
deploycuts a version every time, sodropping the next page token would silently under-report versions after a
few months of use.
undeploygoes throughdryRunAndConfirmDestructive— deleting adeployment permanently breaks its web app URL, so it honours
-y/--forceand fails closed under
--no-input.readonly.yamlandagent-safe.yamlare allow-liststhat fail closed, so the six new leaves are listed explicitly. Without this,
even the read-only
pull,deployments, andversionswould be rejected(and hidden from help) in a baked build.
--readonlyneeds no per-command code:script.googleapis.comis not onthe POST allowlist in
internal/googleapi/read_only.go, so push, deploy,and undeploy are already refused at the transport.
pullstill writeslocal files under
--readonly, matchingdrive download.New flags
appscript deploy:--description,--deployment-idappscript deployments,appscript versions: the standard--max/--page/--all/--fail-emptysetTesting
make cipasses (fmt-check,lint,deadcode,test,docs-check,agent-skills-check).appscript_sync_test.goandappscript_deploy_test.gocoverthe extension mapping, manifest enforcement, non-manifest JSON skipping,
push/pull round trips, pulled-file permissions and path-traversal
containment,
--allacross two pages,nextPageTokenretention in--json, and theundeployconfirmation gate.dryrun_e2e_test.gogains rows forappscript push/pull/deploy/undeploy;TestReadonlySafetyProfileBlocksNestedMutationsgains the two newmutations.
make docs-commands,make agent-skills).Two open questions for maintainers
pushalso require confirmation?UpdateContentreplaces theproject wholesale, so any remote file absent from the local directory is
deleted —
drive sync pushdeliberately advertises "no remote deletes",so this repo is more conservative here. I left push without a prompt so it
stays usable non-interactively, and instead spelled the behaviour out in
the help text. Happy to route it through
dryRunAndConfirmDestructiveifyou'd prefer.
deploy/deployments/undeployare flat siblingverbs over one sub-resource, whereas the rest of the CLI nests
(
drive comments {list,create,delete}). A nestedappscript deployments {list,create,delete}would also make the safetyprofile and
--enable-commandsentries one block instead of threescattered keys. I kept the flat shape to preserve
appscript deployasthe ergonomic one-liner, but I'll restructure if you want consistency.
🤖 Generated with Claude Code