Repository navigation
Conversation
… APC APC's `astro deploy` builds only the 1.x layout: it requires .astro/config.yaml and builds the Dockerfile, whose runtime base installs requirements.txt and packages.txt. Converting a 1.x project kept .astro/config.yaml but retired a FROM-only Dockerfile and the lists it migrated, so an APC user's next deploy passed the project check and failed to build. When the current context is APC (the platform the root mounts, so the same answer `astro deploy` gets), init now keeps those three files, undeclared, writes a .dockerignore for that build's context, and says why under Left to do. The manifest still carries the pin and both lists, and stays a generated-build project, so Astro users and `astro local` are unchanged. Declaring the Dockerfile instead was rejected: it would switch Docker mode and Astro builds to it and drop the generated build for a constraint only APC's deploy has. Co-Authored-By: Claude <noreply@anthropic.com>
Coverage Report for CI Build 38066810338Warning Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes. Coverage increased (+0.3%) to 57.741%Details
Uncovered Changes
Coverage Regressions271 previously-covered lines in 14 files lost coverage.
Coverage Stats
💛 - Coveralls |
…he note agree Review of the first commit found the APC decision and the deploy link made apart. With an APC context, a project whose .astro/config.yaml saved a cuid deployment and workspace kept its 1.x build with an APC note and also got an Astro link. Houston's ids are cuids too, so that pair cannot say which platform it names. Where the project deploys is now settled once, by resolveDeployTarget, and passed to every decision that depends on it. A saved Software release name (the 0.x CLI saved one there) means APC whatever the context says. Otherwise Options.DeploysToAPC, the current context, decides. Under APC the saved target stays a note instead of an Astro link. planRetirements now owns the rule. A Dockerfile that will be built, declared or deployed by APC, keeps requirements.txt and packages.txt, and an APC project's spent Dockerfile is kept. It returns what it kept for APC alone, so the post-filter and the apcBuild field on project1x are gone. The option goes explicitly to the consumers. A Dockerfile kept for APC whose FROM disagrees with --airflow-version or an adopted pin is refused, as a declared one is. The note now names only what was kept for APC, says when the Dockerfile stays for another reason, says a -base runtime installs neither list, and claims the manifest carries only what it does. Also: fileRequirements and filePackages constants. writeContext now shares writeLogin's YAML, refusing hosts included. Co-Authored-By: Claude <noreply@anthropic.com>
… builds
The saved release-name heuristic is gone: an Astro Deployment's namespace has
the same adjective-noun-NNNN shape, and Houston takes custom release names, so
it misclassified both ways. `astro init --deploy-target astro|apc` decides when
given, else the current context does (no context is Astro). It reaches
scaffold as Options.DeploysToAPC, still the one field a caller must set, with
an optional DeployTargetBasis that every APC note and the version refusal use
to say what decided and how to choose the other platform. The note on a saved
deploy target follows the decision: under APC it says the target stays where
APC's deploy reads it, not how to make it an Astro link.
A Dockerfile kept for APC holds the pin to the exact Airflow series its FROM
carries. An Airflow 2 runtime tag names no series, so the runtime catalog
(Options.RuntimeCatalog, asked only then) supplies it: it becomes the pin read
from the Dockerfile and the fix a refusal names. Without a catalog the
conversion says the series is unknown instead of offering "2". A pin naming
only the generation ("3" beside runtime:3.1-12) is refused under APC.
buildsDockerfile is now the one test for "this Dockerfile will be built",
from one fact (a Dockerfile with content), used by the retirement, the
.dockerignore and the refusal.
Co-Authored-By: Claude <noreply@anthropic.com>
…latform advice followable - airflowFromDockerfile reads runtime:3.1, runtime:3 and 3.1-python-3.12 through manifest.ParseRuntimeTag instead of as Airflow 2, so a kept APC Dockerfile pins and refuses at its real series. - apcBuild keeps the series on a manifest.RuntimeTag and disagrees through RuntimeTag.Agrees, adding only the generation-only-pin check. - Options.RuntimeCatalog is the catalog itself: astro init reads it before scaffold, only for an APC conversion, so Plan makes no request. - An unknown Airflow 2 series says whether the catalog was unreadable or does not list the runtime, and the note no longer assumes the pin is "2". - Every undeclared Dockerfile kept under APC is named in the APC note, including one beside a pin to a release of its series. - Advice to convert for the other platform, read after a conversion that a rerun refuses, names what to restore first (removed files, pyproject.toml). - detectRootOptions reads the current context once; hasDockerfile replaces the remaining raw length checks. Co-Authored-By: Claude <noreply@anthropic.com>
…and claim agreement only for an exact series - One rule decides a runtime image for both the version read from a 1.x Dockerfile and the APC pin check: a repository path ending in /runtime (the Airflow 3 lineage) or /astro-runtime (the Airflow 2 lineage), on any registry, so a private mirror is checked like the image it mirrors. - Floating Airflow 3 tags are read only on /runtime: astro-runtime:3.0 is Astro Runtime 3, an Airflow 2 image. - The APC note claims pyproject.toml carries the same Airflow version only when the pin is the exact series the FROM carries. - A floating Airflow 2 tag is not looked up in the catalog or blamed on it: the conversion says the tag floats and how to pin it. - astro init reads the runtime catalog only when the APC build needs it (scaffold.NeedsRuntimeCatalog, offline): a pin-only Dockerfile naming one Airflow 2 runtime build. - A retired pin-only Dockerfile's Python flavor is carried as requires-python, which a generated image honors. Co-Authored-By: Claude <noreply@anthropic.com>
…APC for now There is one project format. astro init converts a 1.x project to it the same way whatever the platform, so --deploy-target, the keep-the-1.x-build branch (its notes, its FROM/pin refusal, the catalog pre-fetch) and the scaffold.Options fields that carried the decision are gone. Astro Private Cloud's astro deploy still builds the 1.x layout, so under an APC context astro init refuses a 1.x project (project.Is1xProject) as a usage error and writes nothing; the project keeps deploying as it is until APC deploys pyproject.toml projects. A new project made under an APC context gets a note that APC cannot deploy it yet. Kept, since they hold on every platform: runtime images read by repository (/runtime, /astro-runtime, on any registry), floating Airflow 3 tags read only on /runtime, a retired pin-only Dockerfile's Python carried as requires-python, hasDockerfile, and the root's single context read. Co-Authored-By: Claude <noreply@anthropic.com>
Out of the way of the home-directory refusal on project-dir-advice-fixes, which goes in right after the directory is resolved. Co-Authored-By: Claude <noreply@anthropic.com>
…same - Revert the pkg/scaffold parser changes (runtime repository rule, floating Airflow 3 tags, Python flavor into requires-python, hasDockerfile) and their docs to v2's; they can come back as their own PR. - Revert cmd/root.go's context read: the platform comes from context.IsCloudContext again, and the refusal names no domain. - project.Project1xUnderAPC is the one account of a 1.x project under an APC context: why it stays as it is, that astro deploy on APC keeps working with it, when conversion comes, and how to convert anyway (switch to an Astro context with astro context switch astronomer.io or astro login, then run astro init again). astro init's refusal, the project-discovery errors of every local command (through AdviseUnderAPC around each RunE) and the astro dev stub all use it, in plain text. - The new-project note under APC is said by the run that made the project only; init again is refused as anywhere. Co-Authored-By: Claude <noreply@anthropic.com>
Moves TestAdviseUnderAPC up the file so it does not collide with the tests project-dir-advice-fixes appends. Co-Authored-By: Claude <noreply@anthropic.com>
So internal/project/project.go's imports stay as v2's and do not collide with project-dir-advice-fixes, which adds one there. Co-Authored-By: Claude <noreply@anthropic.com>
… and subdirectories - internal/project holds one setting, SetUnderAPC, set once by the root in detectRootOptions from the same IsCloudContext check. project1xMessage reads it, so every 1.x hint built from the package (discovery errors, astro local check, Otto, init, the dev stub) gives Project1xUnderAPC's account with no wrapping. AdviseUnderAPC, the RunE wrapping and Deps.DeploysToAPC are gone. - The advice always names the 1.x project's directory. - astro init under APC refuses a directory inside a 1.x project too (Enclosing1xProject, the walk discovery makes), naming the project. - The astro dev stub under APC in a 1.x project: under_apc and the advice in notes (json and text say the same), no astro init as a replacement or in the mapping, a note that astro local needs a converted project; only the convert hint is gated, so a build secret still means --docker. dev-removed schema golden updated for under_apc. Co-Authored-By: Claude <noreply@anthropic.com>
… on the CLI path only - project.Project1xAt reads discovery's own answer (NotFoundError's Project1xDir, NoAstroSectionError's Dir); astro init's refusal and the dev stub under APC both use it. Enclosing1xProject's walk is gone. - Is1xProject never counts the home directory, or ASTRO_HOME, whose .astro/ is the CLI's settings. - The advice says to run astro init in the 1.x project's directory. - astro init under APC allows a new or empty directory inside a 1.x project; it refuses the project itself or an existing non-empty directory in it. - The dev stub under APC names no replacement for a 1.x project; the notes carry the guidance, in json and text. - SetUnderAPC moves to cmd.Execute, so NewRootCmd, which tests build, never sets it; cmd/root.go is back to v2's. Co-Authored-By: Claude <noreply@anthropic.com>
astro init under APC refuses iff the directory it was given is itself a 1.x project (project.Is1xProject, as on v2). No walking up, no Discover, no home-directory exception: Project1xAt, isCLIHome and isNewOrEmpty are gone, and Is1xProject is v2's again. A directory below a 1.x project is allowed; it never touches the 1.x root's Dockerfile or .astro/. The dev stub decides 1.x-ness the same direct way under APC as under Astro, and under APC in a 1.x project gives only the notes: no replacement and no table of astro local commands. Co-Authored-By: Claude <noreply@anthropic.com>
…ed too scaffold.Options gains DeploysToAPC. With it set, Plan and Run refuse a directory that is itself a 1.x project (scaffold.Is1xProject) before reading or writing anything, with a *Convert1xUnderAPCError that matches ErrConvert1xUnderAPC and whose message is Project1xUnderAPCMessage(Dir). internal/project's Is1xProject and Project1xUnderAPC delegate to these, so there is one check and one message. astro init sets the field from project.UnderAPC() and maps the refusal to a usage error, as before. Plan's body moves into plan so its complexity stays under the limit, and Options goes by pointer between internal helpers now that the field puts it over hugeParam's size; Plan and Run stay by value for Desktop. Co-Authored-By: Claude <noreply@anthropic.com>
Reads the 1.x directory inside the context literal rather than on the line after the signature that branch rewrites, and the APC stub test decodes the json by key and reads the text from a text run, so it does not name the payload's Go type, which that branch renames. Co-Authored-By: Claude <noreply@anthropic.com>
True with or without v2's deploy taking only pyproject.toml projects: the shared message now says APC deploys that layout and not yet pyproject.toml projects, and that Astro CLI 1.x keeps deploying it there. The comments and docs/install.md that said v2's astro deploy works with it say the same. Co-Authored-By: Claude <noreply@anthropic.com>
…, and publish a kind of its own - Under DeploysToAPC, scaffold refuses a directory that is a 1.x project or lies inside one (walking up, stopping at an Astro project), naming the 1.x project: a project made inside it would be deployed with it. - Is1xProject does not count a .astro/config.yaml that is the CLI's own settings (no top-level project key, and context, contexts or telemetry), so a stray ~/Dockerfile no longer makes home a 1.x project, while a home directory that holds one still is. - The CLI tells Astro, APC and an unresolved context apart (contextPlatform: an unreadable home config, or a named context it does not hold for a non-Astro domain), and astro init refuses a 1.x project under an unresolved one too, saying to fix the context. - The refusal publishes unsupported_on_platform, exit 1, not usage. - The new-project note under APC is gone from Result.Notes. - The dev stub under APC empties its mapping, so json matches text, and reads the 1.x directory once. - The JSON half of the advice test resets stdout and checks the advice. Co-Authored-By: Claude <noreply@anthropic.com>
- project.Convert1xBlocked(dir) is the one decision: under an APC or unresolved context, the 1.x project dir is in or below (scaffold.Find1xProject, the walk Plan makes), and why. astro init, the discovery errors (NotFoundError, NoAstroSectionError) and the astro dev stub all ask it, so none suggests astro init where init refuses; under an unresolved context they say to fix or switch the context instead (project.Blocked1xMessage). The dev stub walks up too, so a subdirectory gets the root's advice, and publishes context_unresolved beside under_apc. - The walk stops at any directory with a manifest, loading or not, checked before the 1.x layout, so each step loads the manifest once. - In the home directory and ASTRO_HOME, where .astro/ is the CLI's settings, a Dockerfile makes a 1.x project only when .astro/config.yaml names a project; elsewhere the rule is v2's. The content heuristic for the settings file is gone. - contextPlatform takes an Astro ASTRO_DOMAIN as Astro before anything else. - The unresolved refusal names the 1.x project and says to rerun astro init in the directory given. - A successful init under APC prints a notice on stderr in text mode only. - devContext drops is1x; the 1.x directory decides it. dev-removed schema golden updated for context_unresolved. Co-Authored-By: Claude <noreply@anthropic.com>
- A 1.x project is known by content alone: a Dockerfile, and a .astro/config.yaml with a top-level project key, which astro dev init always writes (1.x cmd/airflow.go: CreateProjectConfig, then CFG.ProjectName.SetProjectString). The CLI's home settings name no project, so the HOME/ASTRO_HOME comparison is gone. scaffold.Is1xProject is the one definition; discovery uses it too. Fixtures write project:. - The dev stub uses the nearest enclosing 1.x root in every context (scaffold.Find1xProject) and publishes it as v1_dir. - Every message names the 1.x root and ends "then run astro init in <root>"; where ASTRO_DOMAIN chose the context it says to change or unset ASTRO_DOMAIN instead (project.Context.FromASTRODomain, from one project.SetContext). - NotFoundError and NoAstroSectionError decide the block when they are made; Error() only formats. - init no longer sets opts.DeploysToAPC (it has refused already); that is Desktop's. - apcNotice reworded. dev-removed schema golden updated for v1_dir. Co-Authored-By: Claude <noreply@anthropic.com>
- A 1.x project is what Astro CLI 1.x takes for one (config.IsProjectDir, which its deploy requires): a Dockerfile and a .astro/config.yaml, no manifest, unless that file is the CLI's own settings (a mapping with no project key and context, contexts or telemetry). An empty or unparseable config.yaml counts, as for 1.x. scaffold.Is1xProject is the one definition; internal/project's copy is gone. - "Switch to Astro" is one text, scaffold.SwitchToAstro, naming the domain for astro login (a bare astro login logs back in to APC). - Convert1xBlocked returns the 1.x root in every context; Discover, LoadError and the dev stub use it, so there is one walk and the NoAstroSectionError of a monorepo root names the 1.x project below it. - The dev stub's convert command from below the root is astro init <root>. - project.NewProjectNotice words the notice for APC and for an unresolved context, and for ASTRO_DOMAIN choosing either. - scaffold.HasManifest is exported; internal/project.HasManifest uses it. Co-Authored-By: Claude <noreply@anthropic.com>
- The dev stub from below a 1.x root offers astro init <root> as astro dev init's replacement, in the mapping and in the text's example row too. - An unreadable settings file is named, and the advice is to fix or move it, never to change the context (Context.UnreadableConfig). - Where ASTRO_DOMAIN chose the context, the advice is to set it to an Astro domain (astronomer.io), with "or unset it" only when the saved current context is Astro or there is none (Context.UnsetIsAstro). - NoAstroSectionError outside APC looks for a 1.x project only between start and the pyproject's directory, so a plain package inside a 1.x tree gets the add-[tool.astro] hint; where init refuses, it says what init says. - contextPlatform reads only the current domain and whether its context is saved, never a login, so startup never reaches the keyring. - Discover tracks the 1.x root in its own walk. - Every printed root in a command is quoted by scaffold.ShellQuote, which cmd/config.go's suggestions now use too. - Docs and field comments no longer name the deleted project.Is1xProject. Co-Authored-By: Claude <noreply@anthropic.com>
The refusal and hint tests assumed a temp path needs no quoting, which a Windows path with backslashes always does; they now expect scaffold.ShellQuote(root). Co-Authored-By: Claude <noreply@anthropic.com>
…walk only when it matters - scaffold.ShellQuote uses double quotes on Windows, which cmd.exe and PowerShell both read as one word, and POSIX single quotes elsewhere. - astro local init and astro af|airflow init give init's refusal where init refuses, instead of suggesting it. - astro dev init's replacement stays plain astro init, as 1.x's made a project in the working directory; only the convert line names the root. - Convert1xBlocked checks the context first and walks nothing under Astro; the dev stub walks for the root it names there. Co-Authored-By: Claude <noreply@anthropic.com>
- scaffold.ShellQuote is POSIX single quotes on every GOOS again, as v2's config suggestions had; PowerShell reads them literally. - astro dev init below a 1.x root says to use astro init for a new project here, or astro init <root> to convert the 1.x project in place; json is unchanged. - The af group silences usage, as astro local does, so init's refusal is not followed by a usage block. - devProject uses only Convert1xBlocked's answer under a blocking context and walks once under Astro; Convert1xBlocked names the root only when it refuses (project.BlockingContext). Co-Authored-By: Claude <noreply@anthropic.com>
…d the context once - scaffold.ShellQuote treats a backslash as safe on Windows, so C:\proj prints unquoted; anything else needing quotes keeps POSIX single quotes. The safe set takes the GOOS, so one test covers both. - astro dev init below a 1.x root names the convert command once, in backticks like its siblings. - devProject reads the context once and walks accordingly. - The af group's redundant SilenceUsage and its test are gone; AddCmds already silences usage tree-wide. Co-Authored-By: Claude <noreply@anthropic.com>
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What
There is one project format, and
astro initconverts a 1.x project to it the same way on every platform. This PR adds the one exception, for now: Astro Private Cloud. APC deploys the 1.x layout (a Dockerfile and.astro/config.yaml, with Astro CLI 1.x) and not yet pyproject.toml projects. The PR also makes every hint about a 1.x project agree with that exception.The refusal, in
pkg/scaffoldscaffold.Planandscaffold.Runrefuse whenscaffold.Options.DeploysToAPCis set and the target directory is a 1.x project or lies inside one. They refuse before anything is read or written. The reason for covering directories inside: a project scaffolded inside one (for exampleastro init dags) would be deployed with it by Astro CLI 1.x.How the target is matched:
scaffold.Find1xProjectgoes up from the cleaned absolute target to the filesystem root. It stops at any directory that has a manifest, whether it loads or not, and checks that before the 1.x layout, so each step loads the manifest once. Plan then reports that manifest as it always has.scaffold.Is1xProject, the one definition (the CLI's discovery uses it too), follows what Astro CLI 1.x itself takes for a project. 1.x'sconfig.IsProjectDir(origin/mainconfig/config.go:322) requires.astro/config.yaml, and its deploy requires that throughutils.EnsureProjectDir(cmd/utils/utils.go:25). The rule here is:.astro/config.yaml, with no manifest (scaffold.HasManifest);config.yamlis the CLI's own settings file. That means a YAML mapping with no top-levelprojectkey, and at least one ofcontext,contextsortelemetry(keys from v2'sconfig.CFG). A 1.x project's always hasproject(cmd/airflow.go:677and:680).~/Dockerfilebeside the settings does not make home a 1.x project, while a home directory that is one (HOME=/usr/local/airflowin a 1.x image) is refused.config.yamlcounts, as it does for 1.x (fail closed). A missing one does not.API (additions only;
PlanandRunkeep their by-value signatures)scaffold.Options.DeploysToAPC bool. A caller whose context or platform is APC sets it. The default, false, is today's behaviour.scaffold.ErrConvert1xUnderAPC, the sentinel forerrors.Is.*scaffold.Convert1xUnderAPCError{Dir}, the error value.Diris the 1.x project's directory, and the message isscaffold.Project1xUnderAPCMessage(Dir).scaffold.Is1xProject(dir).internal/project.Is1xProjectdelegates to it, andproject.Project1xUnderAPCdelegates to the message function, so there is one check and one message.scaffold.Find1xProject(dir), the walk.scaffold.Project1xUnderAPCReason(dir), the shared why-it-stays part of the message.scaffold.SwitchToAstro, the one "switch to Astro" text:astro context switch astronomer.io, orastro login astronomer.io. A bareastro loginlogs back in to the current context, which is APC's.scaffold.HasManifest(dir).internal/project.HasManifestuses it.The message
Plain text, naming the 1.x project's directory:
This holds with or without #2295.
One decision for every hint
project.Convert1xBlocked(dir)returns why init refuses (BlockedUnderAPC,BlockedUnresolved, orNotBlocked) and the 1.x project's root. It checks the context first, so under Astro it walks nothing: init walks nothing there. A hint that names the root under Astro walks for itself. The dev stub callsscaffold.Find1xProject, andDiscovertracks the root in its own walk, since no pyproject.toml lies on that path.NoAstroSectionErrorworks the same way:scaffold.Find1xProjectand the oneproject.SetContextthatcmd.Executerecords. Every hint asks it, and discovery's errors keep the answer when they are made, soError()only formats:astro init;NotFoundErrorandNoAstroSectionError, soastro local startand the rest,astro local checkand Otto;astro devstub.So no hint suggests astro init where init refuses. Every message (
project.Blocked1xMessage, and the Astro hints) names the 1.x root and ends "then run astro init in ":ASTRO_DOMAINchose the context, it says to change or unsetASTRO_DOMAINinstead. The table testTestTheDevStubAndInitAgreecovers five contexts (APC, APC viaASTRO_DOMAIN, Astro, unresolved, unresolved viaASTRO_DOMAIN), each at a 1.x root, a subdirectory, and a fresh directory. It checks that the stub and init agree, that the stub's note is init's own message, and thatv1_diris the root.The CLI
Kind and exit code. The refusal is not a mistake in the command line, so it is not usage. It publishes a new documented kind,
unsupported_on_platform(exit 1), listed indocs/architecture.md. Execute's unknown-command tracking runs only for usage errors, so it does not run here.Platform detection (
contextPlatformincmd).cmd.Executesets it once, on the real CLI path only, throughproject.SetUnderAPCandproject.SetContextUnresolved. The cases:ASTRO_DOMAINnames an Astro domainASTRO_DOMAIN(a fresh machine)config.HomeConfigUnreadable)contextPlatformreads only the current domain and whether a context for it is saved (config.Context.ContextExists), never a login, so startup never reaches the keyring; a test pins that. WhereASTRO_DOMAINchose an APC context, the advice is to set it to an Astro domain (astronomer.io). It adds "or unset it" only when the saved current context is Astro, or there is none (Context.UnsetIsAstro).When the context is unresolved,
astro initrefuses the same directories, with the same kind. The message names the 1.x project as the reason, and says to fix the context or switch to one (astro context list, then astro context switch), then rerun astro init in the directory given. Desktop's API stays the one bool.Notice. A successful init under APC, or under an unresolved context, prints one line on stderr, in text mode only (
project.NewProjectNotice). It is worded for that context, and for whetherASTRO_DOMAINchose it. A successful init always means it wrote a new manifest (created, or[tool.astro]adopted into a pyproject.toml). The notice is not inResult.Notesand not in json.No double walk. On the CLI path, init has already refused by the time scaffold runs, so it does not set
opts.DeploysToAPC; that field is for Desktop.The
astro devstub. It finds the nearest enclosing 1.x root in every context.v1_projectnow means inside a 1.x project, and the newv1_dirnames its root. The golden gainsv1_dirandcontext_unresolved.astro init <root>, inconvertand in the text alike. The path is quoted byscaffold.ShellQuote, whichastro config's suggestions now share: POSIX single quotes where quoting is needed, with backslashes safe on Windows, so an ordinary path such asC:\projprints bare.astro dev init's replacement stays plainastro init, and the text offers both, naming the convert command once: "Useastro initto make a new project here, orastro init <root>to convert the 1.x project at in place."astro local init,astro af initandastro airflow initredirect to astro init. Where init refuses, they give its refusal instead.under_apcorcontext_unresolvedset, the same notes, and an emptymapping.Scope
cmd/root.go,cmd/local/root.goandcmd/local/deps.goare as on v2.Possible follow-up: the parser fixes as their own PR. Two fixes were reverted here: floating
/runtimetags read as Airflow 3, and a pin-only Dockerfile's Python flavor carried intorequires-python. Review found points such a PR would need to address:requires-pythonfrom the tag only when the Dockerfile is retired;runtime:/astro-runtime:names;/runtimetag as Airflow 2.Known and inherited
cmd.Executerecords the context once, before the tree runs. The one pre-run that changes the context afterwards isfollowProjectPreRun(cmd/astro/project_scope.go), which setsASTRO_DOMAINto a project's Astro host. It runs only on Astro-tree commands, and it switches from Astro to Astro, so the platform never changes.astro init, the dev stub and the local commands skip the pre-runs entirely. So the snapshot cannot go stale for any decision made here.utils.Locatehas its own 1.x rule (cmd/utils/utils.go:126:has1xLayout(d) && !isHome(d)).scaffold.Is1xProjectis exported, so a follow-up changes that line toif scaffold.Is1xProject(d) {and can drophomeMatcherand its ownhas1xLayout.Is1xProjectloads the manifest once more, which is harmless.internal/project.HasManifest(manifestPresent), and this PR's delegation toscaffold.HasManifesttouches the same body. Whichever merges second keeps Fix the project-directory advice #2283 added (core fixes) #2290's validation-error handling insidescaffold.HasManifest, or keepsmanifestPresent.local.platformset is read ascontext.IsCloudDomainreads it.astro init's refusal isunsupported_on_platform(exit 1). Discovery errors carrying the same advice keepno_project(exit 1).scaffold.Options.DeploysToAPCfrom its platform or context.PlanandRunthen refuse a 1.x project, or a directory inside one, under APC withscaffold.ErrConvert1xUnderAPC(the value is a*scaffold.Convert1xUnderAPCError; message:scaffold.Project1xUnderAPCMessage). The message's last step namesastro init, a CLI command. An unresolved context is the CLI's own handling; Desktop decides its own.Follow-ups (not in this PR)
no_project, versusunsupported_on_platformfor init's refusal.isCLISettingscontent heuristic for the home settings file.find1xBetweenandscaffold.Find1xProject.ShellQuotewrites POSIX single quotes; ordinary Windows paths print bare).astro local initandastro af initunder APC now give init's refusal: they are no longer counted by the unknown-command tracking (which counts usage errors only), and they exit 1, not 2.cmdandcmd/localsuites,TestConfigSuggestionQuotesAValueWithSpacesamong them, do not run on Windows.utils.Locateswitching toscaffold.Is1xProject.Docs
docs/install.md, Step 4, covers the rule, directories inside a 1.x project, the unresolved-context refusal, and the settings-file case.docs/architecture.mdlists the new kind.Tests
pkg/scaffold/apc_test.go:PlanandRunrefuse a 1.x root: the error type, sentinel, message andDirare checked, and nothing is written.Is1xProjectcases: a settings file with contexts only, one with telemetry only, a project key beside contexts, a config with no project key, a.astro/with noconfig.yaml, an unparseableconfig.yaml, a tools-only pyproject, a manifest, and the partial layouts.cmd/local:unsupported_on_platformand exit 1, nothing written, and not usage.astro initfromdags/andastro init freshinside are refused.TestTheAPCAdviceOn1xProjectsIsOneresets stdout and asserts the json error is the APC advice under APC.cmd/contextplatform_test.go: no context, Astro, APC,ASTRO_DOMAINnaming an unsaved APC domain or Astro, and an unreadable home config.e2e/convert_test.go(tier 0): under a real APC context,initandinit dagsare refused with exit 1 and oneunsupported_on_platformobject, and nothing is written. A fresh directory succeeds, with no APC note.Checks run
go build ./...,GOOS=windows go build ./....GOOS=windows go veton./cmd/...,internal/project,pkg/scaffoldande2ewith-tags e2e.make test,make test-submodules,make test-e2e(tier 0), andgo test -race -shuffle=on -count=3 ./cmd/... ./internal/project/....make lint,make lint-submodules,make lint-e2e,make lint-goos,make deadcode.git merge-treeis clean againstv2, Guard removed 1.x commands in CI, and give bundle create/update/delete -o json #2291 and Deploy only pyproject.toml projects in v2 #2295. Each combination builds, vets and passes thecmd,cmd/localandinternal/projecttests (pluspkg/scaffoldwith v2, andcmd/apcwith Deploy only pyproject.toml projects in v2 #2295).Not verified