Fix the import warning for allow-update { none; }; document CLI config paths

jarld import warned that zones with allow-update { none; } use dynamic
updates, though that turns them off (validation already allowed it).
docs/cli.md now gives the CLI configuration file's location on macOS and
Windows, not only ~/.config/jarl/config.yaml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-09 09:05:59 +00:00
1 parent a3a1da54df
commit b22ffee1bb
5 files changed
+59 -4

No files matched your search

+9
View File
@@ -15,6 +15,15 @@ All notable changes to Jarl are documented here. The format follows
and prints the steps to install Jarl and adopt the configuration and prints the steps to install Jarl and adopt the configuration
(docs/development.md). (docs/development.md).
### Fixed
- `jarld import` no longer warns that a zone with `allow-update { none; }`
uses dynamic updates (validation already allowed it).
- docs/cli.md gives the CLI configuration file's location on macOS
(`~/Library/Application Support/jarl/config.yaml`) and Windows
(`%AppData%\jarl\config.yaml`). It was documented only as
`~/.config/jarl/config.yaml`.
## [0.1.0] - 2026-10-09 ## [0.1.0] - 2026-10-09
The first version. The first version.
+13 -2
View File
@@ -44,8 +44,19 @@ jarl whoami
| Changeset to stage into | `-C/--changeset` | `JARL_CHANGESET` | | | Changeset to stage into | `-C/--changeset` | `JARL_CHANGESET` | |
Flags win over the environment, and the environment wins over the file. The Flags win over the environment, and the environment wins over the file. The
file is `~/.config/jarl/config.yaml` (mode 0600), or `--config` / file is `jarl/config.yaml` in the user's configuration directory, or
`JARL_CLI_CONFIG`. `jarl config show` prints it with the token masked. `--config` / `JARL_CLI_CONFIG`:
| System | File |
|--------|------|
| Linux and other Unix | `~/.config/jarl/config.yaml` (`$XDG_CONFIG_HOME/jarl/config.yaml` if set) |
| macOS | `~/Library/Application Support/jarl/config.yaml` |
| Windows | `%AppData%\jarl\config.yaml` (`C:\Users\<you>\AppData\Roaming\jarl\config.yaml`) |
It is written with mode 0600 (on Windows, the AppData folder is already
private to the user). `jarl config show` prints its path and the settings,
with the token masked. On Windows and macOS there is no local jarld, so
always set a server.
Get tokens from the web UI (Account → API tokens), from Get tokens from the web UI (Account → API tokens), from
`jarl token create`, or from `jarld init --token` on the server. `jarl token create`, or from `jarld init --token` on the server.
+2 -1
View File
@@ -322,7 +322,8 @@ jarl status
jarl zone list jarl zone list
``` ```
The CLI's settings are stored in `~/.config/jarl/config.yaml` (mode 0600). The CLI's settings are stored in `~/.config/jarl/config.yaml` (mode 0600;
macOS and Windows use their own folders, see [cli.md](cli.md#setup)).
`JARL_SERVER` and `JARL_TOKEN` override them, which is handy in scripts. `JARL_SERVER` and `JARL_TOKEN` override them, which is handy in scripts.
Create more tokens in the web UI (Account → API tokens) or with Create more tokens in the web UI (Account → API tokens) or with
`jarl token create NAME`. See [cli.md](cli.md). `jarl token create NAME`. See [cli.md](cli.md).
+16 -1
View File
@@ -477,7 +477,7 @@ func (im *importer) zone(s *namedconf.Stmt, v *model.View) error {
} }
report.Records, report.Skipped = n, skipped report.Records, report.Skipped = n, skipped
for _, o := range opts { for _, o := range opts {
if kw := o.Keyword(); kw == "allow-update" || kw == "update-policy" { if kw := o.Keyword(); kw == "update-policy" || kw == "allow-update" && !allowsNone(o) {
im.warn("zone %s uses %s (dynamic updates); Jarl rewrites zone files and won't apply such zones until %s is removed", dnsname.Display(z.Name), kw, kw) im.warn("zone %s uses %s (dynamic updates); Jarl rewrites zone files and won't apply such zones until %s is removed", dnsname.Display(z.Name), kw, kw)
} }
} }
@@ -735,3 +735,18 @@ func ParseZoneText(zone, text string) (*ParsedZone, error) {
} }
return pz, nil return pz, nil
} }
// allowsNone says whether an address match list option is { none; }
// (allow-update { none; } turns dynamic updates off).
func allowsNone(o *namedconf.Stmt) bool {
b := o.FirstBlock()
if b == nil {
return false
}
for _, s := range b.Stmts {
if namedconf.Compact(s.Items) != "none" {
return false
}
}
return true
}
+19
View File
@@ -146,3 +146,22 @@ func TestParseTTL(t *testing.T) {
} }
} }
} }
func TestImportAllowUpdateWarning(t *testing.T) {
dir := t.TempDir()
os.WriteFile(filepath.Join(dir, "x.db"), []byte("$TTL 300\n@ IN SOA ns1.x.test. h.x.test. 1 2 3 4 5\n@ IN NS ns1\nns1 IN A 192.0.2.1\n"), 0o644)
for conf, want := range map[string]bool{
`allow-update { none; };`: false,
`allow-update { 127.0.0.1; };`: true,
`update-policy local;`: true,
} {
os.WriteFile(filepath.Join(dir, "named.conf"), []byte(`zone "x.test" { type primary; file "`+dir+`/x.db"; `+conf+` };`), 0o644)
_, rep, err := Import(context.Background(), filepath.Join(dir, "named.conf"), Options{Alloc: seqAlloc()})
if err != nil {
t.Fatal(err)
}
if got := len(rep.Warnings) > 0; got != want {
t.Errorf("%s: warnings = %v", conf, rep.Warnings)
}
}
}