Skip to content

Commit 41a40d1

Browse files
committed
Redesign checksum config and add migration tool
1 parent 0a47604 commit 41a40d1

27 files changed

Lines changed: 1140 additions & 82 deletions

docs/docs/advanced_usage.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,17 @@ But we have one downside - run `npm install` may take some time and we do not wa
9191

9292
Checksums allow you to know when some of the files have changed and made a decision based on that.
9393

94-
When you add `checksum` directive to a command - `lets` will calculate checksum from all of the files listed in `checksum` and put `LETS_CHECKSUM` env variable to command env.
94+
When you add `checksum.files` directive to a command - `lets` will calculate checksum from all of the listed files and put `LETS_CHECKSUM` env variable to command env.
9595

9696
`LETS_CHECKSUM` will have a checksum value.
9797

9898
We then can store this checksum somewhere in the file and check that stored checksum with a checksum from env.
9999

100-
Fortunately, `lets` have an option for that - `persist_checksum`.
100+
Fortunately, `lets` have an option for that - `checksum.persist`.
101101

102-
If `persist_cheksum` used with `checksum` `lets` will store new checksum to `.lets` dir and each time you run a command `lets` will check if stored checksum changed from the one from env.
102+
If `checksum.persist` is used with `checksum.files`, `lets` will store new checksum to `.lets` dir and each time you run a command `lets` will check if stored checksum changed from the one from env.
103103

104-
While using `persist_checksum`, `lets` will add new env variable to command env - `LETS_CHECKUM_CHANGED`.
104+
While using `checksum.persist`, `lets` will add new env variable to command env - `LETS_CHECKSUM_CHANGED`.
105105

106106
You can learn more about checksum in [Checksum section](config.md#checksum)
107107

@@ -112,8 +112,9 @@ commands:
112112
build-deps:
113113
description: Install project dependencies
114114
checksum:
115-
- package.json
116-
persist_checksum: true
115+
files:
116+
- package.json
117+
persist: true
117118
cmd: |
118119
if [[ ${LETS_CHECKSUM_CHANGED} == true ]]; then
119120
npm install
@@ -142,8 +143,9 @@ commands:
142143
build-deps:
143144
description: Install project dependencies
144145
checksum:
145-
- package.json
146-
persist_checksum: true
146+
files:
147+
- package.json
148+
persist: true
147149
cmd: |
148150
if [[ ${LETS_CHECKSUM_CHANGED} == true ]]; then
149151
npm install

docs/docs/changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ title: Changelog
55

66
## [Unreleased](https://github.com/lets-cli/lets/releases/tag/v0.0.X)
77

8+
* `[Added]` Add `checksum.files`, `checksum.sh`, and `checksum.persist` command checksum syntax while keeping the old checksum format compatible.
9+
* `[Added]` Add `lets self fix` config migration command with `--dry-run` preview output for deprecated checksum syntax.
810
* `[Refactoring]` Use Go 1.26 `errors.AsType` for type-safe error unwrapping.
911

1012
## [0.0.61](https://github.com/lets-cli/lets/releases/tag/v0.0.61)

docs/docs/cli.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ title: CLI options
1919
|`-v, --version`|||version for lets|
2020

2121
Upgrade the lets binary with `lets self upgrade`.
22+
23+
Migrate deprecated config syntax with `lets self fix`. Use `lets self fix --dry-run` to print migrated config content before writing files.

docs/docs/config.md

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -789,11 +789,11 @@ commands:
789789

790790
`key: checksum`
791791

792-
`type: array of string | mapping string => array of string`
792+
`type: object`
793793

794794
Checksum used for computing file hashes. It is useful when you depend on some files content changes.
795795

796-
In `checksum` you can specify:
796+
In `checksum.files` you can specify:
797797

798798
- a list of file names
799799
- a list of file regexp patterns (parsed via go `path/filepath.Glob`)
@@ -814,10 +814,11 @@ If checksum is a mapping, e.g:
814814
commands:
815815
build:
816816
checksum:
817-
deps:
818-
- package.json
819-
doc:
820-
- Readme.md
817+
files:
818+
deps:
819+
- package.json
820+
doc:
821+
- Readme.md
821822
```
822823

823824
Resulting env will be:
@@ -830,29 +831,42 @@ Checksum is calculated with `sha1`.
830831

831832
If you specify patterns, `lets` will try to find all matches and will calculate checksum of that files.
832833

834+
You can use `checksum.sh` instead of `checksum.files` when you need to provide the checksum value from a shell command:
835+
836+
```yaml
837+
commands:
838+
build:
839+
checksum:
840+
sh: git rev-parse HEAD
841+
cmd: docker build -t myrepo/app:${LETS_CHECKSUM} .
842+
```
843+
844+
`checksum.files` and `checksum.sh` are mutually exclusive.
845+
833846
Example:
834847

835848
```yaml
836849
shell: bash
837850
commands:
838851
app-build:
839852
checksum:
840-
- requirements-*.txt
853+
files:
854+
- requirements-*.txt
841855
cmd: |
842856
docker pull myrepo/app:${LETS_CHECKSUM}
843857
docker run --rm myrepo/app${LETS_CHECKSUM} python -m app
844858
```
845859

846860

847-
### `persist_checksum`
861+
### `checksum.persist`
848862

849-
`key: persist_checksum`
863+
`key: checksum.persist`
850864

851865
`type: bool`
852866

853867
This feature is useful when you want to know that something has changed between two executions of a command.
854868

855-
`persist_checksum` can be used only if `checksum` declared for command.
869+
`checksum.persist` can be used only if `checksum.files` or `checksum.sh` declared for command.
856870

857871
If set to `true`, each run all calculated checksums will be stored to disk.
858872

@@ -869,12 +883,13 @@ Example:
869883
```yaml
870884
commands:
871885
build:
872-
persist_checksum: true
873886
checksum:
874-
deps:
875-
- package.json
876-
doc:
877-
- Readme.md
887+
persist: true
888+
files:
889+
deps:
890+
- package.json
891+
doc:
892+
- Readme.md
878893
```
879894

880895
Resulting env will be:
@@ -887,6 +902,8 @@ Resulting env will be:
887902
* `LETS_CHECKSUM_DOC_CHANGED` - is checksum of doc files changed
888903
* `LETS_CHECKSUM_CHANGED` - is checksum of all checksums (deps and doc) changed
889904

905+
`checksum` as a direct list/map and command-level `persist_checksum` are deprecated compatibility syntax. Use `lets self fix` to migrate local configs to `checksum.files` and `checksum.persist`.
906+
890907
### `ref`
891908

892909
`key: ref`

docs/static/schema.json

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,7 @@
132132
"type": "boolean"
133133
},
134134
"checksum": {
135-
"type": "array",
136-
"items": {
137-
"type": "string"
138-
}
135+
"$ref": "#/definitions/command_checksum"
139136
},
140137
"env": {
141138
"$ref": "#/definitions/env"
@@ -237,6 +234,61 @@
237234
}
238235
}
239236
]
237+
},
238+
"checksum_files": {
239+
"oneOf": [
240+
{
241+
"type": "array",
242+
"items": {
243+
"type": "string"
244+
}
245+
},
246+
{
247+
"type": "object",
248+
"additionalProperties": {
249+
"type": "array",
250+
"items": {
251+
"type": "string"
252+
}
253+
}
254+
}
255+
]
256+
},
257+
"command_checksum": {
258+
"oneOf": [
259+
{
260+
"$ref": "#/definitions/checksum_files"
261+
},
262+
{
263+
"type": "object",
264+
"properties": {
265+
"files": {
266+
"$ref": "#/definitions/checksum_files"
267+
},
268+
"sh": {
269+
"type": "string",
270+
"description": "Shell command that prints checksum value."
271+
},
272+
"persist": {
273+
"type": "boolean",
274+
"description": "Persist checksum and expose LETS_CHECKSUM_CHANGED variables."
275+
}
276+
},
277+
"oneOf": [
278+
{
279+
"required": [
280+
"files"
281+
]
282+
},
283+
{
284+
"required": [
285+
"sh"
286+
]
287+
}
288+
],
289+
"additionalProperties": false
290+
}
291+
]
240292
}
241293
},
242294
"additionalProperties": false

internal/checksum/checksum.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import (
66
"encoding/hex"
77
"fmt"
88
"os"
9+
"os/exec"
910
"path/filepath"
1011
"sort"
12+
"strings"
1113

1214
"github.com/lets-cli/lets/internal/set"
1315
"github.com/lets-cli/lets/internal/util"
@@ -147,6 +149,44 @@ func CalculateChecksumFromSources(workDir string, checksumSources map[string][]s
147149
return checksumMap, nil
148150
}
149151

152+
func CalculateChecksumFromConfig(
153+
workDir string,
154+
checksumSources map[string][]string,
155+
shell string,
156+
script string,
157+
env map[string]string,
158+
) (map[string]string, error) {
159+
if script != "" {
160+
result, err := CalculateChecksumFromScript(workDir, shell, script, env)
161+
if err != nil {
162+
return nil, err
163+
}
164+
165+
return map[string]string{DefaultChecksumKey: result}, nil
166+
}
167+
168+
return CalculateChecksumFromSources(workDir, checksumSources)
169+
}
170+
171+
func CalculateChecksumFromScript(workDir string, shell string, script string, env map[string]string) (string, error) {
172+
cmd := exec.Command(shell, "-c", script)
173+
cmd.Dir = workDir
174+
175+
envList := os.Environ()
176+
for key, value := range env {
177+
envList = append(envList, fmt.Sprintf("%s=%s", key, value))
178+
}
179+
180+
cmd.Env = envList
181+
182+
out, err := cmd.Output()
183+
if err != nil {
184+
return "", fmt.Errorf("can not get output from checksum.sh script: %s: %w", script, err)
185+
}
186+
187+
return strings.TrimSpace(string(out)), nil
188+
}
189+
150190
func ReadChecksumFromDisk(checksumsDir, cmdName, checksumName string) (string, error) {
151191
_, checksumFilePath := getChecksumPath(checksumsDir, cmdName, checksumName)
152192

internal/checksum/checksum_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,16 @@ func TestCalculateChecksumFromListOrMap(t *testing.T) {
136136
)
137137
}
138138
}
139+
140+
func TestCalculateChecksumFromScript(t *testing.T) {
141+
tempDir := t.TempDir()
142+
143+
got, err := CalculateChecksumFromScript(tempDir, "bash", `printf "checksum-value\n"`, map[string]string{})
144+
if err != nil {
145+
t.Fatalf("unexpected error: %s", err)
146+
}
147+
148+
if got != "checksum-value" {
149+
t.Fatalf("unexpected checksum: %s", got)
150+
}
151+
}

internal/cmd/fix.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package cmd
2+
3+
import (
4+
"os"
5+
6+
"github.com/lets-cli/lets/internal/config/migrate"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
func initFixCommand() *cobra.Command {
11+
var dryRun bool
12+
13+
fixCmd := &cobra.Command{
14+
Use: "fix",
15+
Short: "Apply lets config migrations",
16+
Args: cobra.NoArgs,
17+
RunE: func(cmd *cobra.Command, args []string) error {
18+
configName, err := cmd.Root().Flags().GetString("config")
19+
if err != nil {
20+
return err
21+
}
22+
23+
if configName == "" {
24+
configName = os.Getenv("LETS_CONFIG")
25+
}
26+
27+
_, err = migrate.Fix(configName, os.Getenv("LETS_CONFIG_DIR"), dryRun, cmd.OutOrStdout())
28+
29+
return err
30+
},
31+
}
32+
33+
fixCmd.Flags().BoolVar(&dryRun, "dry-run", false, "print migrated config without writing files")
34+
35+
return fixCmd
36+
}

internal/cmd/self.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func initSelfCmd(rootCmd *cobra.Command, version string, openURL func(string) er
2525
rootCmd.AddCommand(selfCmd)
2626

2727
selfCmd.AddCommand(initDocCommand(openURL))
28+
selfCmd.AddCommand(initFixCommand())
2829
selfCmd.AddCommand(initLspCommand(version))
2930
selfCmd.AddCommand(initUpgradeCommand(version))
3031
}

0 commit comments

Comments
 (0)