From 5a5c3cb41598e043741c34cf596ce323d7a203f5 Mon Sep 17 00:00:00 2001 From: eitsupi Date: Sat, 26 Sep 2026 02:45:41 +0000 Subject: [PATCH] fix(lockfile): reject unsupported versions before sync --- cmd/dbc/lockfile.go | 3 ++ cmd/dbc/lockfile_test.go | 78 ++++++++++++++++++++++++++++++++++++++++ cmd/dbc/sync_test.go | 20 +++++++++++ 3 files changed, 101 insertions(+) create mode 100644 cmd/dbc/lockfile_test.go diff --git a/cmd/dbc/lockfile.go b/cmd/dbc/lockfile.go index 46e951b8..f5b52dc1 100644 --- a/cmd/dbc/lockfile.go +++ b/cmd/dbc/lockfile.go @@ -51,6 +51,9 @@ func loadLockFile(p string) (LockFile, error) { if err := toml.NewDecoder(f).Decode(&lf); err != nil { return lf, fmt.Errorf("error decoding lock file %s: %w", p, err) } + if lf.Version != lockFileVersion { + return lf, fmt.Errorf("unsupported lock file version %d; this version of dbc supports lock file version %d", lf.Version, lockFileVersion) + } lf.lockinfo = make(map[string]lockInfo) for _, d := range lf.Drivers { diff --git a/cmd/dbc/lockfile_test.go b/cmd/dbc/lockfile_test.go new file mode 100644 index 00000000..c127cee3 --- /dev/null +++ b/cmd/dbc/lockfile_test.go @@ -0,0 +1,78 @@ +// Copyright 2026 Columnar Technologies Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLoadLockFileVersion(t *testing.T) { + tests := []struct { + name string + contents string + wantErr string + }{ + { + name: "version 1", + contents: "version = 1\n[[drivers]]\nname = 'test-driver-1'\nversion = '1.0.0'\n", + }, + { + name: "version 2", + contents: "version = 2\n", + wantErr: "unsupported lock file version 2", + }, + { + name: "version 2 with driver metadata", + contents: "version = 2\nrevision = 0\n" + + "[[drivers]]\nname = 'test-driver-1'\nversion = '1.0.0'\n" + + "[drivers.source]\ntype = 'packslip'\n" + + "[[drivers.artifacts]]\nformat = 'tar.gz'\n", + wantErr: "unsupported lock file version 2", + }, + { + name: "future version", + contents: "version = 99\n", + wantErr: "unsupported lock file version 99", + }, + { + name: "missing version", + contents: "[[drivers]]\nname = 'test-driver-1'\nversion = '1.0.0'\n", + wantErr: "unsupported lock file version 0", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + path := filepath.Join(t.TempDir(), "dbc.lock") + require.NoError(t, os.WriteFile(path, []byte(tt.contents), 0o644)) + + lock, err := loadLockFile(path) + if tt.wantErr != "" { + require.ErrorContains(t, err, tt.wantErr) + assert.Nil(t, lock.lockinfo) + return + } + + require.NoError(t, err) + assert.Equal(t, lockFileVersion, lock.Version) + assert.Equal(t, "1.0.0", lock.lockinfo["test-driver-1"].Version.String()) + }) + } +} diff --git a/cmd/dbc/sync_test.go b/cmd/dbc/sync_test.go index e9f41027..38338983 100644 --- a/cmd/dbc/sync_test.go +++ b/cmd/dbc/sync_test.go @@ -46,6 +46,26 @@ func (suite *SubcommandTestSuite) TestSync() { suite.validateOutput("✓ test-driver-1-1.1.0 already installed\r\n\rDone!\r\n", "", suite.runCmd(m)) } +func (suite *SubcommandTestSuite) TestSyncRejectsUnsupportedLockFileWithoutRewriting() { + listPath := filepath.Join(suite.tempdir, "dbc.toml") + lockPath := filepath.Join(suite.tempdir, "dbc.lock") + lockContents := []byte("version = 2\nrevision = 0\n" + + "[[drivers]]\nname = 'test-driver-1'\nversion = '1.0.0'\n" + + "[drivers.source]\ntype = 'packslip'\n" + + "[[drivers.artifacts]]\nformat = 'tar.gz'\n") + suite.Require().NoError(os.WriteFile(listPath, []byte("[drivers]\n[drivers.test-driver-1]\n"), 0o644)) + suite.Require().NoError(os.WriteFile(lockPath, lockContents, 0o644)) + + m := SyncCmd{Path: listPath}.GetModelCustom(testBaseModel()) + output := suite.runCmdErr(m) + suite.Contains(output, "unsupported lock file version 2") + + actual, err := os.ReadFile(lockPath) + suite.Require().NoError(err) + suite.Equal(lockContents, actual) + suite.NoFileExists(filepath.Join(suite.tempdir, "test-driver-1.toml")) +} + func (suite *SubcommandTestSuite) TestSyncWithVersion() { tests := []struct { driver string