From df840c97bb8d46524ff7a833c6aefdbc3461f420 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Mon, 4 Mar 2024 13:59:55 -0800 Subject: [PATCH 01/18] refactor(packages): rename "source" to "bake" package I am refactoring towards exposing more of the bake functionality as a package. --- internal/commands/bake.go | 4 +- internal/commands/bake_test.go | 6 +- pkg/{source/bake_record.go => bake/record.go} | 76 +++++++++---------- .../record_test.go} | 29 ++++--- 4 files changed, 57 insertions(+), 58 deletions(-) rename pkg/{source/bake_record.go => bake/record.go} (69%) rename pkg/{source/bake_record_test.go => bake/record_test.go} (86%) diff --git a/internal/commands/bake.go b/internal/commands/bake.go index 3b582a24..a6d5659b 100644 --- a/internal/commands/bake.go +++ b/internal/commands/bake.go @@ -21,8 +21,8 @@ import ( "github.com/pivotal-cf/kiln/internal/builder" "github.com/pivotal-cf/kiln/internal/commands/flags" "github.com/pivotal-cf/kiln/internal/helper" + "github.com/pivotal-cf/kiln/pkg/bake" "github.com/pivotal-cf/kiln/pkg/cargo" - "github.com/pivotal-cf/kiln/pkg/source" ) //counterfeiter:generate -o ./fakes/interpolator.go --fake-name Interpolator . interpolator @@ -233,7 +233,7 @@ func writeBakeRecord(tileFilepath, metadataFilepath string, productTemplate []by if err != nil { return fmt.Errorf("failed to calculate checksum: %w", err) } - b, err := source.NewBakeRecord(tileSum, productTemplate) + b, err := bake.NewRecord(tileSum, productTemplate) if err != nil { return fmt.Errorf("failed to create bake record: %w", err) } diff --git a/internal/commands/bake_test.go b/internal/commands/bake_test.go index a9c022a8..2f6df56b 100644 --- a/internal/commands/bake_test.go +++ b/internal/commands/bake_test.go @@ -2,6 +2,7 @@ package commands_test import ( "errors" + "github.com/pivotal-cf/kiln/pkg/bake" "log" "os" "path/filepath" @@ -22,7 +23,6 @@ import ( "github.com/pivotal-cf/kiln/internal/commands/fakes" "github.com/pivotal-cf/kiln/internal/commands/flags" "github.com/pivotal-cf/kiln/pkg/proofing" - "github.com/pivotal-cf/kiln/pkg/source" ) var _ = Describe("Bake", func() { @@ -1034,7 +1034,7 @@ func TestBakeDescription(t *testing.T) { t.Fatalf("expected Options struct field %s", fieldName) } description := field.Tag.Get("description") - if !strings.Contains(description, source.BakeRecordsDirectory) { - t.Errorf("expected description to mention bake records directory %q", source.BakeRecordsDirectory) + if !strings.Contains(description, bake.RecordsDirectory) { + t.Errorf("expected description to mention bake records directory %q", bake.RecordsDirectory) } } diff --git a/pkg/source/bake_record.go b/pkg/bake/record.go similarity index 69% rename from pkg/source/bake_record.go rename to pkg/bake/record.go index b8e41b55..8cf4d7c7 100644 --- a/pkg/source/bake_record.go +++ b/pkg/bake/record.go @@ -1,4 +1,4 @@ -package source +package bake import ( "encoding/json" @@ -17,11 +17,11 @@ import ( "github.com/Masterminds/semver/v3" ) -// BakeRecordsDirectory should be a sibling to Kilnfile or base.yml -const BakeRecordsDirectory = "bake_records" +// RecordsDirectory should be a sibling to Kilnfile or base.yml +const RecordsDirectory = "bake_records" -// BakeRecord is created by the function -type BakeRecord struct { +// Record is created by the function +type Record struct { // SourceRevision is the commit checked out when the build was run SourceRevision string `yaml:"source_revision" json:"source_revision"` @@ -39,8 +39,8 @@ type BakeRecord struct { FileChecksum string `yaml:"file_checksum,omitempty" json:"file_checksum,omitempty"` } -// NewBakeRecord parses build information from an OpsManger Product Template (aka metadata/metadata.yml) -func NewBakeRecord(fileChecksum string, productTemplateBytes []byte) (BakeRecord, error) { +// NewRecord parses build information from an OpsManger Product Template (aka metadata/metadata.yml) +func NewRecord(fileChecksum string, productTemplateBytes []byte) (Record, error) { var productTemplate struct { ProductVersion string `yaml:"product_version"` KilnMetadata builder.KilnMetadata `yaml:"kiln_metadata"` @@ -49,10 +49,10 @@ func NewBakeRecord(fileChecksum string, productTemplateBytes []byte) (BakeRecord err := yaml.Unmarshal(productTemplateBytes, &productTemplate) if productTemplate.KilnMetadata.KilnVersion == "" { - return BakeRecord{}, fmt.Errorf("failed to parse build information from product template: kiln_metadata.kiln_version not found") + return Record{}, fmt.Errorf("failed to parse build information from product template: kiln_metadata.kiln_version not found") } - return BakeRecord{ + return Record{ SourceRevision: productTemplate.KilnMetadata.MetadataGitSHA, Version: productTemplate.ProductVersion, KilnVersion: productTemplate.KilnMetadata.KilnVersion, @@ -61,14 +61,35 @@ func NewBakeRecord(fileChecksum string, productTemplateBytes []byte) (BakeRecord }, err } -func (b BakeRecord) Name() string { +func ReadRecords(dir fs.FS) ([]Record, error) { + infos, err := fs.ReadDir(dir, RecordsDirectory) + if err != nil { + return nil, err + } + builds := make([]Record, 0, len(infos)) + for _, info := range infos { + buf, err := fs.ReadFile(dir, path.Join(RecordsDirectory, info.Name())) + if err != nil { + return nil, err + } + var build Record + if err := json.Unmarshal(buf, &build); err != nil { + return nil, err + } + builds = append(builds, build) + } + slices.SortFunc(builds, compareMultiple(Record.CompareVersion, Record.CompareTileName)) + return builds, nil +} + +func (b Record) Name() string { if b.TileName != "" { return path.Join(b.TileName, b.Version) } return b.Version } -func (b BakeRecord) CompareVersion(o BakeRecord) int { +func (b Record) CompareVersion(o Record) int { bv, err := semver.NewVersion(b.Version) if err != nil { return strings.Compare(b.Version, o.Version) @@ -80,22 +101,22 @@ func (b BakeRecord) CompareVersion(o BakeRecord) int { return bv.Compare(ov) } -func (b BakeRecord) CompareTileName(o BakeRecord) int { +func (b Record) CompareTileName(o Record) int { return strings.Compare(b.TileName, o.TileName) } -func (b BakeRecord) IsDevBuild() bool { +func (b Record) IsDevBuild() bool { return b.SourceRevision == builder.DirtyWorktreeSHAValue } -func (b BakeRecord) WriteFile(tileSourceDirectory string) error { +func (b Record) WriteFile(tileSourceDirectory string) error { if b.Version == "" { return fmt.Errorf("missing required version field") } if b.IsDevBuild() { - return fmt.Errorf("will not write development builds to %s directory", BakeRecordsDirectory) + return fmt.Errorf("will not write development builds to %s directory", RecordsDirectory) } - if err := os.MkdirAll(filepath.Join(tileSourceDirectory, BakeRecordsDirectory), 0o766); err != nil { + if err := os.MkdirAll(filepath.Join(tileSourceDirectory, RecordsDirectory), 0o766); err != nil { return err } buf, err := json.MarshalIndent(b, "", " ") @@ -106,34 +127,13 @@ func (b BakeRecord) WriteFile(tileSourceDirectory string) error { if b.TileName != "" { fileName = b.TileName + "-" + fileName } - outputFilepath := filepath.Join(tileSourceDirectory, BakeRecordsDirectory, fileName) + outputFilepath := filepath.Join(tileSourceDirectory, RecordsDirectory, fileName) if _, err := os.Stat(outputFilepath); err == nil { return fmt.Errorf("tile bake record already exists for %s", b.Name()) } return os.WriteFile(outputFilepath, buf, 0o644) } -func ReadBakeRecords(dir fs.FS) ([]BakeRecord, error) { - infos, err := fs.ReadDir(dir, BakeRecordsDirectory) - if err != nil { - return nil, err - } - builds := make([]BakeRecord, 0, len(infos)) - for _, info := range infos { - buf, err := fs.ReadFile(dir, path.Join(BakeRecordsDirectory, info.Name())) - if err != nil { - return nil, err - } - var build BakeRecord - if err := json.Unmarshal(buf, &build); err != nil { - return nil, err - } - builds = append(builds, build) - } - slices.SortFunc(builds, compareMultiple(BakeRecord.CompareVersion, BakeRecord.CompareTileName)) - return builds, nil -} - func compareMultiple[T any](cmp ...func(a, b T) int) func(a, b T) int { return func(a, b T) int { var result int diff --git a/pkg/source/bake_record_test.go b/pkg/bake/record_test.go similarity index 86% rename from pkg/source/bake_record_test.go rename to pkg/bake/record_test.go index bb841376..b7cd1e8a 100644 --- a/pkg/source/bake_record_test.go +++ b/pkg/bake/record_test.go @@ -1,6 +1,7 @@ -package source_test +package bake_test import ( + "github.com/pivotal-cf/kiln/pkg/bake" "os" "path/filepath" "testing" @@ -8,14 +9,12 @@ import ( "github.com/pivotal-cf/kiln/internal/builder" "github.com/stretchr/testify/require" - - "github.com/pivotal-cf/kiln/pkg/source" ) func TestBuild(t *testing.T) { t.Run("when creating a bake record from a product template", func(t *testing.T) { // language=yaml - b, err := source.NewBakeRecord("some-peach-jam", []byte(` + b, err := bake.NewRecord("some-peach-jam", []byte(` product_name: p-each product_version: some-product-version kiln_metadata: @@ -24,7 +23,7 @@ kiln_metadata: tile_name: srt `)) require.NoError(t, err) - require.Equal(t, source.BakeRecord{ + require.Equal(t, bake.Record{ Version: "some-product-version", KilnVersion: "some-kiln-version", SourceRevision: "some-tile-source-revision", @@ -35,7 +34,7 @@ kiln_metadata: t.Run("when the product template is missing kiln_metadata", func(t *testing.T) { // language=yaml - _, err := source.NewBakeRecord("some-peach-jam", []byte(` + _, err := bake.NewRecord("some-peach-jam", []byte(` product_name: p-each product_version: some-product-version `)) @@ -45,7 +44,7 @@ product_version: some-product-version t.Run("write one file", func(t *testing.T) { dir := t.TempDir() - b := source.BakeRecord{ + b := bake.Record{ TileName: "p-each", SourceRevision: "some-revision", Version: "1.2.3", @@ -54,7 +53,7 @@ product_version: some-product-version require.NoError(t, b.WriteFile(dir)) - buf, err := os.ReadFile(filepath.Join(dir, source.BakeRecordsDirectory, "p-each-1.2.3.json")) + buf, err := os.ReadFile(filepath.Join(dir, bake.RecordsDirectory, "p-each-1.2.3.json")) require.NoError(t, err) require.JSONEq(t, `{"source_revision":"some-revision", "tile_name":"p-each", "version":"1.2.3", "kiln_version": "some-version"}`, string(buf)) @@ -63,7 +62,7 @@ product_version: some-product-version t.Run("when the record is missing the version field", func(t *testing.T) { dir := t.TempDir() - b := source.BakeRecord{ + b := bake.Record{ Version: "", } @@ -73,7 +72,7 @@ product_version: some-product-version t.Run("when a record is marked as developement", func(t *testing.T) { dir := t.TempDir() - b := source.BakeRecord{ + b := bake.Record{ Version: "1.2.3", SourceRevision: builder.DirtyWorktreeSHAValue, } @@ -84,13 +83,13 @@ product_version: some-product-version t.Run("write only required some fields", func(t *testing.T) { dir := t.TempDir() - b := source.BakeRecord{ + b := bake.Record{ Version: "some-version", } require.NoError(t, b.WriteFile(dir)) - buf, err := os.ReadFile(filepath.Join(dir, source.BakeRecordsDirectory, "some-version.json")) + buf, err := os.ReadFile(filepath.Join(dir, bake.RecordsDirectory, "some-version.json")) require.NoError(t, err) require.JSONEq(t, `{"source_revision":"", "version":"some-version", "kiln_version": ""}`, string(buf)) @@ -99,7 +98,7 @@ product_version: some-product-version t.Run("when a build record with the same version already exists", func(t *testing.T) { dir := t.TempDir() - b := source.BakeRecord{ + b := bake.Record{ TileName: "some-tile", Version: "some-version", } @@ -111,7 +110,7 @@ product_version: some-product-version t.Run("when read builds", func(t *testing.T) { dir := t.TempDir() - bs := []source.BakeRecord{ + bs := []bake.Record{ { // non standard semver TileName: "p-each", SourceRevision: "some-hash-000", @@ -167,7 +166,7 @@ product_version: some-product-version require.NoError(t, b.WriteFile(dir)) } - result, err := source.ReadBakeRecords(os.DirFS(dir)) + result, err := bake.ReadRecords(os.DirFS(dir)) require.NoError(t, err) require.Equal(t, bs, result, "the builds are in order and contain all the info") From 4d342176e9c914e32516eb1e5244fe4a1afada77 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Mon, 4 Mar 2024 14:09:19 -0800 Subject: [PATCH 02/18] feat(bake package): add NewRecordFromFile method --- pkg/bake/record.go | 37 +++++++++++++++++++++++++++++++-- pkg/bake/record_test.go | 15 ++++++++++++- pkg/bake/testdata/tile.pivotal | Bin 0 -> 86524 bytes 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 pkg/bake/testdata/tile.pivotal diff --git a/pkg/bake/record.go b/pkg/bake/record.go index 8cf4d7c7..ddf765e5 100644 --- a/pkg/bake/record.go +++ b/pkg/bake/record.go @@ -1,8 +1,11 @@ package bake import ( + "crypto/sha256" + "encoding/hex" "encoding/json" "fmt" + "io" "io/fs" "os" "path" @@ -12,9 +15,10 @@ import ( "gopkg.in/yaml.v3" - "github.com/pivotal-cf/kiln/internal/builder" - "github.com/Masterminds/semver/v3" + + "github.com/pivotal-cf/kiln/internal/builder" + "github.com/pivotal-cf/kiln/pkg/tile" ) // RecordsDirectory should be a sibling to Kilnfile or base.yml @@ -61,6 +65,31 @@ func NewRecord(fileChecksum string, productTemplateBytes []byte) (Record, error) }, err } +// NewRecordFromFile parses the product template and pulls the kiln metadata out. +// The SHA256 sum is also calculated for the file. +func NewRecordFromFile(tileFilepath string) (Record, error) { + metadata, err := tile.ReadMetadataFromFile(tileFilepath) + if err != nil { + return Record{}, err + } + checksum, err := fileChecksum(tileFilepath) + if err != nil { + return Record{}, err + } + return NewRecord(checksum, metadata) +} + +func fileChecksum(name string) (string, error) { + f, err := os.Open(name) + if err != nil { + return "", err + } + defer closeAndIgnoreError(f) + sum := sha256.New() + _, err = io.Copy(sum, f) + return hex.EncodeToString(sum.Sum(nil)), err +} + func ReadRecords(dir fs.FS) ([]Record, error) { infos, err := fs.ReadDir(dir, RecordsDirectory) if err != nil { @@ -146,3 +175,7 @@ func compareMultiple[T any](cmp ...func(a, b T) int) func(a, b T) int { return result } } + +func closeAndIgnoreError(closer io.Closer) { + _ = closer.Close() +} diff --git a/pkg/bake/record_test.go b/pkg/bake/record_test.go index b7cd1e8a..7b8dbf23 100644 --- a/pkg/bake/record_test.go +++ b/pkg/bake/record_test.go @@ -2,6 +2,7 @@ package bake_test import ( "github.com/pivotal-cf/kiln/pkg/bake" + "github.com/stretchr/testify/assert" "os" "path/filepath" "testing" @@ -11,7 +12,19 @@ import ( "github.com/stretchr/testify/require" ) -func TestBuild(t *testing.T) { +func TestNewRecordFromFile(t *testing.T) { + tilePath := filepath.Join("testdata", "tile.pivotal") + record, err := bake.NewRecordFromFile(tilePath) + require.NoError(t, err) + assert.Equal(t, bake.Record{ + Version: "0.2.0-dev", + SourceRevision: "5874e0f81d0af47922716a7c69a08bcdead13348", + KilnVersion: "70092ce5ce6c0fd6b2434add95774e2612af5a51", + FileChecksum: "7490ba0b736c262ee7dc433c423c4f95ad838b014769d8465c50e445967d2735", + }, record) +} + +func TestNewRecord(t *testing.T) { t.Run("when creating a bake record from a product template", func(t *testing.T) { // language=yaml b, err := bake.NewRecord("some-peach-jam", []byte(` diff --git a/pkg/bake/testdata/tile.pivotal b/pkg/bake/testdata/tile.pivotal new file mode 100644 index 0000000000000000000000000000000000000000..da550e0ab4b4a40f14609edfc7f215e5700a50ab GIT binary patch literal 86524 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�anD_m@IL)|+9 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
_Nuu#Za3-g7bT+C*#)~n~x1x1!i;v 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%0Adyqz0j2?I+x#oPE@Cw>j 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 literal 0 HcmV?d00001 From 0e5e726ae29b5b15c57d74616c46709ea3dd9aa2 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Tue, 5 Mar 2024 12:47:15 -0800 Subject: [PATCH 03/18] feat(bake command): make product template checksum stable between kiln versions this commit removes the kiln version from kiln_metadata field in the baked product templates the bake records can now be used to determine the kiln version for a particular bake --- internal/acceptance/bake/bake_test.go | 4 -- .../workflows/baking_a_tile.feature | 4 +- internal/builder/interpolator.go | 1 - internal/builder/kiln_metadata.go | 2 - internal/builder/kiln_metadata_test.go | 7 --- .../append_kiln_metadata/output_metadata.yml | 1 - .../replace_kiln_metadata/output_metadata.yml | 1 - internal/commands/bake.go | 8 +-- internal/commands/bake_test.go | 7 +-- pkg/bake/record.go | 52 ++++++++++--------- pkg/bake/record_test.go | 4 +- 11 files changed, 38 insertions(+), 53 deletions(-) diff --git a/internal/acceptance/bake/bake_test.go b/internal/acceptance/bake/bake_test.go index e83e8675..d3ad1f6a 100644 --- a/internal/acceptance/bake/bake_test.go +++ b/internal/acceptance/bake/bake_test.go @@ -874,7 +874,6 @@ some_runtime_configs: serial: false selected_value: "235" kiln_metadata: - kiln_version: 0.0.0-dev.0+acceptance metadata_git_sha: %s ` @@ -899,7 +898,6 @@ stemcell_criteria: requires_cpi: false enable_patch_security_updates: true kiln_metadata: - kiln_version: 0.0.0-dev.0+acceptance metadata_git_sha: %s ` @@ -921,7 +919,6 @@ additional_stemcells_criteria: - os: windows version: "2019.4" kiln_metadata: - kiln_version: 0.0.0-dev.0+acceptance metadata_git_sha: %s ` @@ -940,6 +937,5 @@ stemcell_criteria: os: ubuntu-trusty version: "3215.4" kiln_metadata: - kiln_version: 0.0.0-dev.0+acceptance metadata_git_sha: %s ` diff --git a/internal/acceptance/workflows/baking_a_tile.feature b/internal/acceptance/workflows/baking_a_tile.feature index 65ba0da6..1c7313eb 100644 --- a/internal/acceptance/workflows/baking_a_tile.feature +++ b/internal/acceptance/workflows/baking_a_tile.feature @@ -15,8 +15,8 @@ Feature: As a developer, I want to bake a tile And "bake_records/0.2.0-dev.json" contains substring: "version": "0.2.0-dev" And "bake_records/0.2.0-dev.json" contains substring: "source_revision": "bc3ac24e192ba06a2eca19381ad785ec7069e0d0" And "bake_records/0.2.0-dev.json" contains substring: "kiln_version": "0.0.0+acceptance-tests" - And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "342fd94f1f3ccaaa1b964977067a1d89915b2c6957242d18bd1ef39babcda915" - And "tile-0.2.0-dev.pivotal" has sha256 sum "342fd94f1f3ccaaa1b964977067a1d89915b2c6957242d18bd1ef39babcda915" + And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" + And "tile-0.2.0-dev.pivotal" has sha256 sum "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" Scenario: it reads directory configuration from Kilnfile Given I have a tile source directory "testdata/tiles/non-standard-paths" diff --git a/internal/builder/interpolator.go b/internal/builder/interpolator.go index a17ad3b1..fe81b15e 100644 --- a/internal/builder/interpolator.go +++ b/internal/builder/interpolator.go @@ -37,7 +37,6 @@ type Interpolator struct{} type InterpolateInput struct { Version string - KilnVersion string BOSHVariables map[string]any Variables map[string]any ReleaseManifests map[string]any diff --git a/internal/builder/kiln_metadata.go b/internal/builder/kiln_metadata.go index 12016e13..92d00862 100644 --- a/internal/builder/kiln_metadata.go +++ b/internal/builder/kiln_metadata.go @@ -10,14 +10,12 @@ import ( type KilnMetadata struct { MetadataGitSHA string `yaml:"metadata_git_sha"` - KilnVersion string `yaml:"kiln_version"` TileName string `yaml:"tile_name,omitempty"` } func newKilnMetadata(input InterpolateInput) KilnMetadata { m := KilnMetadata{ - KilnVersion: input.KilnVersion, MetadataGitSHA: input.MetadataGitSHA, } diff --git a/internal/builder/kiln_metadata_test.go b/internal/builder/kiln_metadata_test.go index 64de3e3c..e5fb22ce 100644 --- a/internal/builder/kiln_metadata_test.go +++ b/internal/builder/kiln_metadata_test.go @@ -22,7 +22,6 @@ func Test_setKilnMetadata(t *testing.T) { result, err := setKilnMetadata(inputMetadataYML, KilnMetadata{ MetadataGitSHA: "some-commit-sha", - KilnVersion: "some-kiln-version", }) require.NoError(t, err) assert.Equal(t, string(outputMetadataYML), string(result)) @@ -33,7 +32,6 @@ func Test_setKilnMetadata(t *testing.T) { func Test_newKilnMetadata(t *testing.T) { t.Run("when tile name is set", func(t *testing.T) { input := InterpolateInput{ - KilnVersion: "some-version", MetadataGitSHA: "some-sha", Variables: map[string]any{ TileNameVariable: "some-tile", @@ -43,7 +41,6 @@ func Test_newKilnMetadata(t *testing.T) { km := newKilnMetadata(input) require.Equal(t, KilnMetadata{ - KilnVersion: "some-version", MetadataGitSHA: "some-sha", TileName: "some-tile", }, km) @@ -51,7 +48,6 @@ func Test_newKilnMetadata(t *testing.T) { t.Run("when no tile name is set", func(t *testing.T) { input := InterpolateInput{ - KilnVersion: "some-version", MetadataGitSHA: "some-sha", Variables: nil, } @@ -59,7 +55,6 @@ func Test_newKilnMetadata(t *testing.T) { km := newKilnMetadata(input) require.Equal(t, KilnMetadata{ - KilnVersion: "some-version", MetadataGitSHA: "some-sha", TileName: "", }, km) @@ -67,7 +62,6 @@ func Test_newKilnMetadata(t *testing.T) { t.Run("when no tile name is the wrong type", func(t *testing.T) { input := InterpolateInput{ - KilnVersion: "some-version", MetadataGitSHA: "some-sha", Variables: map[string]any{ TileNameVariable: 11, @@ -77,7 +71,6 @@ func Test_newKilnMetadata(t *testing.T) { km := newKilnMetadata(input) require.Equal(t, KilnMetadata{ - KilnVersion: "some-version", MetadataGitSHA: "some-sha", TileName: "", }, km) diff --git a/internal/builder/testdata/append_kiln_metadata/output_metadata.yml b/internal/builder/testdata/append_kiln_metadata/output_metadata.yml index f029ed4e..a3452208 100644 --- a/internal/builder/testdata/append_kiln_metadata/output_metadata.yml +++ b/internal/builder/testdata/append_kiln_metadata/output_metadata.yml @@ -83,4 +83,3 @@ stemcell_criteria: version: "621.256" kiln_metadata: metadata_git_sha: some-commit-sha - kiln_version: some-kiln-version diff --git a/internal/builder/testdata/replace_kiln_metadata/output_metadata.yml b/internal/builder/testdata/replace_kiln_metadata/output_metadata.yml index 3deb4724..c28ec10f 100644 --- a/internal/builder/testdata/replace_kiln_metadata/output_metadata.yml +++ b/internal/builder/testdata/replace_kiln_metadata/output_metadata.yml @@ -59,7 +59,6 @@ job_types: release: bpm kiln_metadata: metadata_git_sha: some-commit-sha - kiln_version: some-kiln-version label: Hello metadata_version: 2.7.0 minimum_version_for_upgrade: 0.1.0 diff --git a/internal/commands/bake.go b/internal/commands/bake.go index a6d5659b..11c2ed89 100644 --- a/internal/commands/bake.go +++ b/internal/commands/bake.go @@ -140,7 +140,7 @@ func NewBake(fs billy.Filesystem, releasesService baking.ReleasesService, outLog } } -type writeBakeRecordSignature func(string, string, []byte) error +type writeBakeRecordSignature func(string, string, string, []byte) error type Bake struct { interpolator interpolator @@ -228,7 +228,7 @@ func NewBakeWithInterfaces(interpolator interpolator, tileWriter tileWriter, out var _ writeBakeRecordSignature = writeBakeRecord -func writeBakeRecord(tileFilepath, metadataFilepath string, productTemplate []byte) error { +func writeBakeRecord(kilnVersion, tileFilepath, metadataFilepath string, productTemplate []byte) error { tileSum, err := tileChecksum(tileFilepath) if err != nil { return fmt.Errorf("failed to calculate checksum: %w", err) @@ -237,6 +237,7 @@ func writeBakeRecord(tileFilepath, metadataFilepath string, productTemplate []by if err != nil { return fmt.Errorf("failed to create bake record: %w", err) } + b.KilnVersion = kilnVersion abs, err := filepath.Abs(metadataFilepath) if err != nil { return fmt.Errorf("failed to find tile root for bake records: %w", err) @@ -529,7 +530,6 @@ func (b Bake) Execute(args []string) error { } input := builder.InterpolateInput{ - KilnVersion: b.KilnVersion, Version: b.Options.Version, Variables: templateVariables, BOSHVariables: boshVariables, @@ -575,7 +575,7 @@ func (b Bake) Execute(args []string) error { } if b.Options.IsFinal { - if err := b.writeBakeRecord(b.Options.OutputFile, b.Options.Metadata, interpolatedMetadata); err != nil { + if err := b.writeBakeRecord(b.KilnVersion, b.Options.OutputFile, b.Options.Metadata, interpolatedMetadata); err != nil { return err } } diff --git a/internal/commands/bake_test.go b/internal/commands/bake_test.go index 2f6df56b..f94cbf29 100644 --- a/internal/commands/bake_test.go +++ b/internal/commands/bake_test.go @@ -1013,13 +1013,14 @@ release_sources: }) type fakeWriteBakeRecordFunc struct { - tilePath, recordPath string - productTemplate []byte + kilnVersion, tilePath, recordPath string + productTemplate []byte err error } -func (f *fakeWriteBakeRecordFunc) call(tilePath, recordPath string, productTemplate []byte) error { +func (f *fakeWriteBakeRecordFunc) call(kilnVersion, tilePath, recordPath string, productTemplate []byte) error { + f.kilnVersion = kilnVersion f.tilePath = tilePath f.recordPath = recordPath f.productTemplate = productTemplate diff --git a/pkg/bake/record.go b/pkg/bake/record.go index ddf765e5..ca74498f 100644 --- a/pkg/bake/record.go +++ b/pkg/bake/record.go @@ -33,7 +33,7 @@ type Record struct { Version string `yaml:"version" json:"version"` // KilnVersion is the Kiln CLI version - KilnVersion string `yaml:"kiln_version" json:"kiln_version"` + KilnVersion string `yaml:"kiln_version,omitempty" json:"kiln_version,omitempty"` // TileName might record the tile name used in baking because sometimes multiple tiles can be generated from the same tile source directory // An example of this is the two Tanzu Application Service tiles with different topologies listed on TanzuNetwork. @@ -51,18 +51,20 @@ func NewRecord(fileChecksum string, productTemplateBytes []byte) (Record, error) } err := yaml.Unmarshal(productTemplateBytes, &productTemplate) + if err != nil { + return Record{}, err + } - if productTemplate.KilnMetadata.KilnVersion == "" { - return Record{}, fmt.Errorf("failed to parse build information from product template: kiln_metadata.kiln_version not found") + if productTemplate.KilnMetadata.MetadataGitSHA == "" { + return Record{}, fmt.Errorf("failed to parse build information from product template: kiln_metadata.metadata_git_sha not found") } return Record{ SourceRevision: productTemplate.KilnMetadata.MetadataGitSHA, Version: productTemplate.ProductVersion, - KilnVersion: productTemplate.KilnMetadata.KilnVersion, TileName: productTemplate.KilnMetadata.TileName, FileChecksum: fileChecksum, - }, err + }, nil } // NewRecordFromFile parses the product template and pulls the kiln metadata out. @@ -111,54 +113,54 @@ func ReadRecords(dir fs.FS) ([]Record, error) { return builds, nil } -func (b Record) Name() string { - if b.TileName != "" { - return path.Join(b.TileName, b.Version) +func (record Record) Name() string { + if record.TileName != "" { + return path.Join(record.TileName, record.Version) } - return b.Version + return record.Version } -func (b Record) CompareVersion(o Record) int { - bv, err := semver.NewVersion(b.Version) +func (record Record) CompareVersion(o Record) int { + bv, err := semver.NewVersion(record.Version) if err != nil { - return strings.Compare(b.Version, o.Version) + return strings.Compare(record.Version, o.Version) } ov, err := semver.NewVersion(o.Version) if err != nil { - return strings.Compare(b.Version, o.Version) + return strings.Compare(record.Version, o.Version) } return bv.Compare(ov) } -func (b Record) CompareTileName(o Record) int { - return strings.Compare(b.TileName, o.TileName) +func (record Record) CompareTileName(o Record) int { + return strings.Compare(record.TileName, o.TileName) } -func (b Record) IsDevBuild() bool { - return b.SourceRevision == builder.DirtyWorktreeSHAValue +func (record Record) IsDevBuild() bool { + return record.SourceRevision == builder.DirtyWorktreeSHAValue } -func (b Record) WriteFile(tileSourceDirectory string) error { - if b.Version == "" { +func (record Record) WriteFile(tileSourceDirectory string) error { + if record.Version == "" { return fmt.Errorf("missing required version field") } - if b.IsDevBuild() { + if record.IsDevBuild() { return fmt.Errorf("will not write development builds to %s directory", RecordsDirectory) } if err := os.MkdirAll(filepath.Join(tileSourceDirectory, RecordsDirectory), 0o766); err != nil { return err } - buf, err := json.MarshalIndent(b, "", " ") + buf, err := json.MarshalIndent(record, "", " ") if err != nil { return err } - fileName := b.Version + ".json" - if b.TileName != "" { - fileName = b.TileName + "-" + fileName + fileName := record.Version + ".json" + if record.TileName != "" { + fileName = record.TileName + "-" + fileName } outputFilepath := filepath.Join(tileSourceDirectory, RecordsDirectory, fileName) if _, err := os.Stat(outputFilepath); err == nil { - return fmt.Errorf("tile bake record already exists for %s", b.Name()) + return fmt.Errorf("tile bake record already exists for %s", record.Name()) } return os.WriteFile(outputFilepath, buf, 0o644) } diff --git a/pkg/bake/record_test.go b/pkg/bake/record_test.go index 7b8dbf23..7cb28af5 100644 --- a/pkg/bake/record_test.go +++ b/pkg/bake/record_test.go @@ -19,7 +19,6 @@ func TestNewRecordFromFile(t *testing.T) { assert.Equal(t, bake.Record{ Version: "0.2.0-dev", SourceRevision: "5874e0f81d0af47922716a7c69a08bcdead13348", - KilnVersion: "70092ce5ce6c0fd6b2434add95774e2612af5a51", FileChecksum: "7490ba0b736c262ee7dc433c423c4f95ad838b014769d8465c50e445967d2735", }, record) } @@ -38,7 +37,6 @@ kiln_metadata: require.NoError(t, err) require.Equal(t, bake.Record{ Version: "some-product-version", - KilnVersion: "some-kiln-version", SourceRevision: "some-tile-source-revision", TileName: "srt", FileChecksum: "some-peach-jam", @@ -105,7 +103,7 @@ product_version: some-product-version buf, err := os.ReadFile(filepath.Join(dir, bake.RecordsDirectory, "some-version.json")) require.NoError(t, err) - require.JSONEq(t, `{"source_revision":"", "version":"some-version", "kiln_version": ""}`, string(buf)) + require.JSONEq(t, `{"source_revision":"", "version":"some-version"}`, string(buf)) }) t.Run("when a build record with the same version already exists", func(t *testing.T) { From 01bd85792ae8b7b59762e6f81b78386fb18139a5 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Tue, 5 Mar 2024 16:13:13 -0800 Subject: [PATCH 04/18] feat(bake command): record tile directory in bake record this will allow tile authors to move things around and still get consistent builds --- .../workflows/baking_a_tile.feature | 1 + .../scenario/step_funcs_tile_source_code.go | 7 +- internal/commands/bake.go | 12 ++- pkg/bake/record.go | 38 ++++++- pkg/bake/record_test.go | 102 ++++++++++++++++++ 5 files changed, 156 insertions(+), 4 deletions(-) diff --git a/internal/acceptance/workflows/baking_a_tile.feature b/internal/acceptance/workflows/baking_a_tile.feature index 1c7313eb..2c18e0ba 100644 --- a/internal/acceptance/workflows/baking_a_tile.feature +++ b/internal/acceptance/workflows/baking_a_tile.feature @@ -14,6 +14,7 @@ Feature: As a developer, I want to bake a tile | releases/hello-release-0.2.3.tgz | And "bake_records/0.2.0-dev.json" contains substring: "version": "0.2.0-dev" And "bake_records/0.2.0-dev.json" contains substring: "source_revision": "bc3ac24e192ba06a2eca19381ad785ec7069e0d0" + And "bake_records/0.2.0-dev.json" contains substring: "tile_directory": "." And "bake_records/0.2.0-dev.json" contains substring: "kiln_version": "0.0.0+acceptance-tests" And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" And "tile-0.2.0-dev.pivotal" has sha256 sum "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" diff --git a/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go b/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go index d65b44e3..4f0afb86 100644 --- a/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go +++ b/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go @@ -92,7 +92,12 @@ func iHaveATileDirectory(ctx context.Context, tileDirectory string) (context.Con return ctx, fmt.Errorf("failed to create new copy of tile directory: %w", err) } - dir, err := copyTileDirectory(tmpDir, tileDirectory) + resolvedDir, err := filepath.EvalSymlinks(tmpDir) + if err != nil { + return ctx, fmt.Errorf("failed to resolve symlinks for test tile directory: %w", err) + } + + dir, err := copyTileDirectory(resolvedDir, tileDirectory) if err != nil { return ctx, err } diff --git a/internal/commands/bake.go b/internal/commands/bake.go index 11c2ed89..61bfc7eb 100644 --- a/internal/commands/bake.go +++ b/internal/commands/bake.go @@ -237,13 +237,21 @@ func writeBakeRecord(kilnVersion, tileFilepath, metadataFilepath string, product if err != nil { return fmt.Errorf("failed to create bake record: %w", err) } + b.KilnVersion = kilnVersion + abs, err := filepath.Abs(metadataFilepath) if err != nil { return fmt.Errorf("failed to find tile root for bake records: %w", err) } - dir := filepath.Dir(abs) - if err := b.WriteFile(dir); err != nil { + tileDir := filepath.Dir(abs) + + b, err = b.SetTileDirectory(tileDir) + if err != nil { + return err + } + + if err := b.WriteFile(tileDir); err != nil { return fmt.Errorf("failed to write bake record: %w", err) } return nil diff --git a/pkg/bake/record.go b/pkg/bake/record.go index ca74498f..7ad297c1 100644 --- a/pkg/bake/record.go +++ b/pkg/bake/record.go @@ -1,6 +1,7 @@ package bake import ( + "bytes" "crypto/sha256" "encoding/hex" "encoding/json" @@ -8,6 +9,7 @@ import ( "io" "io/fs" "os" + "os/exec" "path" "path/filepath" "slices" @@ -39,8 +41,11 @@ type Record struct { // An example of this is the two Tanzu Application Service tiles with different topologies listed on TanzuNetwork. TileName string `yaml:"tile_name,omitempty" json:"tile_name,omitempty"` - // FileChecksum is the SHA256 checksum of the baked tile. + // FileChecksum may be the SHA256 checksum of the baked tile. FileChecksum string `yaml:"file_checksum,omitempty" json:"file_checksum,omitempty"` + + // TileDirectory may be the directory containing tile source. + TileDirectory string `yaml:"tile_directory,omitempty" json:"tile_directory,omitempty"` } // NewRecord parses build information from an OpsManger Product Template (aka metadata/metadata.yml) @@ -165,6 +170,37 @@ func (record Record) WriteFile(tileSourceDirectory string) error { return os.WriteFile(outputFilepath, buf, 0o644) } +func (record Record) SetTileDirectory(tileSourceDirectory string) (Record, error) { + absoluteTileSourceDirectory, err := filepath.Abs(tileSourceDirectory) + if err != nil { + return record, err + } + repoRoot, err := repositoryRoot(tileSourceDirectory) + if err != nil { + return record, err + } + repoRoot += string(filepath.Separator) + absoluteTileSourceDirectory += string(filepath.Separator) + if !strings.HasPrefix(absoluteTileSourceDirectory, repoRoot) { + return record, fmt.Errorf("expected tile directory %q to either be or be a child of the repository root directory %q", absoluteTileSourceDirectory, repoRoot) + } + relativeTilePath := strings.TrimPrefix(absoluteTileSourceDirectory, repoRoot) + record.TileDirectory = filepath.ToSlash(filepath.Clean(relativeTilePath)) + return record, nil +} + +func repositoryRoot(dir string) (string, error) { + var out bytes.Buffer + cmd := exec.Command("git", "rev-parse", "--show-toplevel") + cmd.Dir = dir + cmd.Stdout = &out + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("failed to get HEAD revision hash: %w", err) + } + return strings.TrimSpace(out.String()), nil +} + func compareMultiple[T any](cmp ...func(a, b T) int) func(a, b T) int { return func(a, b T) int { var result int diff --git a/pkg/bake/record_test.go b/pkg/bake/record_test.go index 7cb28af5..0ea8d2a8 100644 --- a/pkg/bake/record_test.go +++ b/pkg/bake/record_test.go @@ -4,6 +4,7 @@ import ( "github.com/pivotal-cf/kiln/pkg/bake" "github.com/stretchr/testify/assert" "os" + "os/exec" "path/filepath" "testing" @@ -183,3 +184,104 @@ product_version: some-product-version require.Equal(t, bs, result, "the builds are in order and contain all the info") }) } + +func TestBakeRecord_SetTileDirectory(t *testing.T) { + t.Run("when run in a subdirectory", func(t *testing.T) { + // this test makes use of the go test characteristic that tests are run in the repository root + + repoRoot := createAndMoveToTemporaryTileDirectory(t, "peach", "pear") + createGitRepository(t, repoRoot) + + record, err := bake.Record{}.SetTileDirectory(".") + require.NoError(t, err) + + assert.Equal(t, "peach/pear", record.TileDirectory) + }) + + t.Run("when run in the root", func(t *testing.T) { + // this test makes use of the go test characteristic that tests are run in the repository root + + repoRoot := createAndMoveToTemporaryTileDirectory(t) + createGitRepository(t, repoRoot) + + record, err := bake.Record{}.SetTileDirectory(".") + require.NoError(t, err) + + assert.Equal(t, ".", record.TileDirectory) + }) + + t.Run("when passed a directory not in the repository", func(t *testing.T) { + // this test makes use of the go test characteristic that tests are run in the repository root + + repoRoot := createAndMoveToTemporaryTileDirectory(t) + createGitRepository(t, repoRoot) + + someOtherDir := t.TempDir() + + _, err := bake.Record{}.SetTileDirectory(someOtherDir) + require.Error(t, err, "either be or be a child of the repository root directory") + }) + + t.Run("when passed a sub directory", func(t *testing.T) { + // this test makes use of the go test characteristic that tests are run in the repository root + + repoRoot := createAndMoveToTemporaryTileDirectory(t) + createGitRepository(t, repoRoot) + subDir := filepath.Join("peach", "pear") + require.NoError(t, os.MkdirAll(subDir, 0766)) + + record, err := bake.Record{}.SetTileDirectory(subDir) + require.NoError(t, err) + + assert.Equal(t, "peach/pear", record.TileDirectory) + }) + + t.Run("when executed from a non repository child directory", func(t *testing.T) { + // this test makes use of the go test characteristic that tests are run in the repository root + + repoDir, err := filepath.EvalSymlinks(t.TempDir()) + require.NoError(t, err) + createGitRepository(t, repoDir) + + record, err := bake.Record{}.SetTileDirectory(repoDir) + require.NoError(t, err) + + assert.Equal(t, ".", record.TileDirectory) + }) +} + +func createAndMoveToTemporaryTileDirectory(t *testing.T, subDirectory ...string) string { + t.Helper() + testDir, err := os.Getwd() + require.NoError(t, err) + repoDir := t.TempDir() + + require.NoError(t, os.Chdir(repoDir)) + + if len(subDirectory) > 0 { + subDir := filepath.Join(subDirectory...) + require.NoError(t, os.MkdirAll(subDir, 0766)) + require.NoError(t, os.Chdir(subDir)) + } + + t.Cleanup(func() { + if err := os.Chdir(testDir); err != nil { + t.Fatalf("failed to go back to test dir: %s", err) + } + }) + + // EvalSymlinks is required on GOOS=darwin because /var (an ancestor of the returned value from t.TempDir) is a + // symbolic link to /private/var. `git rev-parse --show-toplevel` returns a path with symbolic links resolved. + // To simplify test assertions and the implementation, we resolve thos symbolic links here in the helper. + resolved, err := filepath.EvalSymlinks(repoDir) + require.NoError(t, err) + + return resolved +} + +func createGitRepository(t *testing.T, dir string) { + t.Helper() + gitInit := exec.Command("git", "init") + gitInit.Dir = dir + require.NoError(t, gitInit.Run()) +} From 4fe8cb0456c912b9c40c5dc870550cb25c456b3b Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Tue, 5 Mar 2024 17:42:00 -0800 Subject: [PATCH 05/18] feat: add re-bake command this command takes a bake record and rebuilds a tile --- .../workflows/baking_a_tile.feature | 15 ++- .../tiles/bake-record/tile/.gitignore | 3 + .../testdata/tiles/bake-record/tile/Kilnfile | 6 ++ .../tiles/bake-record/tile/Kilnfile.lock | 9 ++ .../bake-record/tile/bake_records/0.1.0.json | 7 ++ .../testdata/tiles/bake-record/tile/base.yml | 84 +++++++++++++++++ .../testdata/tiles/bake-record/tile/icon.png | Bin 0 -> 62860 bytes .../tiles/bake-record/tile/releases/.gitkeep | 0 .../testdata/tiles/bake-record/tile/version | 1 + .../acceptance/workflows/using_kiln.feature | 1 + internal/commands/rebake.go | 87 ++++++++++++++++++ main.go | 1 + pkg/bake/record.go | 29 ++++++ 13 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 internal/acceptance/workflows/testdata/tiles/bake-record/tile/.gitignore create mode 100644 internal/acceptance/workflows/testdata/tiles/bake-record/tile/Kilnfile create mode 100644 internal/acceptance/workflows/testdata/tiles/bake-record/tile/Kilnfile.lock create mode 100644 internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json create mode 100644 internal/acceptance/workflows/testdata/tiles/bake-record/tile/base.yml create mode 100644 internal/acceptance/workflows/testdata/tiles/bake-record/tile/icon.png create mode 100644 internal/acceptance/workflows/testdata/tiles/bake-record/tile/releases/.gitkeep create mode 100644 internal/acceptance/workflows/testdata/tiles/bake-record/tile/version create mode 100644 internal/commands/rebake.go diff --git a/internal/acceptance/workflows/baking_a_tile.feature b/internal/acceptance/workflows/baking_a_tile.feature index 2c18e0ba..61282f68 100644 --- a/internal/acceptance/workflows/baking_a_tile.feature +++ b/internal/acceptance/workflows/baking_a_tile.feature @@ -16,8 +16,8 @@ Feature: As a developer, I want to bake a tile And "bake_records/0.2.0-dev.json" contains substring: "source_revision": "bc3ac24e192ba06a2eca19381ad785ec7069e0d0" And "bake_records/0.2.0-dev.json" contains substring: "tile_directory": "." And "bake_records/0.2.0-dev.json" contains substring: "kiln_version": "0.0.0+acceptance-tests" - And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" - And "tile-0.2.0-dev.pivotal" has sha256 sum "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" + And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "25a0bac10db840b33f2e281e7bb82627ce6c8f8d7c157af7d41d1e6d45d0cbd0" + And "tile-0.2.0-dev.pivotal" has sha256 sum "25a0bac10db840b33f2e281e7bb82627ce6c8f8d7c157af7d41d1e6d45d0cbd0" Scenario: it reads directory configuration from Kilnfile Given I have a tile source directory "testdata/tiles/non-standard-paths" @@ -25,3 +25,14 @@ Feature: As a developer, I want to bake a tile | bake | | --stub-releases | Then a Tile is created + + Scenario: it bakes a tile from a bake record + Given I have a tile source directory "testdata/tiles/bake-record" + When I invoke kiln + | re-bake | + | --output-file=tile-0.1.0.pivotal | + | tile/bake_records/0.1.0.json | + Then a Tile is created + And the Tile contains + | metadata/metadata.yml | + | releases/bpm-1.1.21.tgz | \ No newline at end of file diff --git a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/.gitignore b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/.gitignore new file mode 100644 index 00000000..e36ada76 --- /dev/null +++ b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/.gitignore @@ -0,0 +1,3 @@ +releases/*.tgz +*.pivotal +bake_records/*.json \ No newline at end of file diff --git a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/Kilnfile b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/Kilnfile new file mode 100644 index 00000000..6ce4e51f --- /dev/null +++ b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/Kilnfile @@ -0,0 +1,6 @@ +--- +slug: min +release_sources: + - type: bosh.io +releases: + - name: bpm diff --git a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/Kilnfile.lock b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/Kilnfile.lock new file mode 100644 index 00000000..ca38b36a --- /dev/null +++ b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/Kilnfile.lock @@ -0,0 +1,9 @@ +releases: + - name: bpm + sha1: e8abe19ec186962828de843f8f281cddb6141904 + version: 1.1.21 + remote_source: bosh.io + remote_path: https://bosh.io/d/github.com/cloudfoundry/bpm-release?v=1.1.21 +stemcell_criteria: + os: ubuntu-xenial + version: "621.0" diff --git a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json new file mode 100644 index 00000000..075e189d --- /dev/null +++ b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json @@ -0,0 +1,7 @@ +{ + "source_revision": "4b872e02af39ac7a025af39e1da55b3e0a3cbfe0", + "version": "0.1.0", + "kiln_version": "0.0.0+acceptance-tests", + "tile_directory": "tile", + "file_checksum": "9ef9c2dfcc87688a35d880bfc9141b724a8d61e6de0bbf2b16d226ac57182a98" +} \ No newline at end of file diff --git a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/base.yml b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/base.yml new file mode 100644 index 00000000..039df82f --- /dev/null +++ b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/base.yml @@ -0,0 +1,84 @@ +--- +name: bpm +label: BPM +description: It just installs the BOSH Package Manager on an instance +icon_image: $( icon ) + +metadata_version: "2.7.0" +minimum_version_for_upgrade: 0.1.0 +product_version: $( version ) +provides_product_versions: + - name: bpm + version: $( version ) + +rank: 90 +serial: false + +releases: + - $( release "bpm" ) + +stemcell_criteria: $( stemcell ) + +job_types: + - name: hello-server + label: Server + resource_label: Server + description: HTTP Server + + templates: + - name: bpm + release: bpm + manifest: {} + + static_ip: 1 + dynamic_ip: 0 + + max_in_flight: 1 + single_az_only: true + + instance_definition: + name: instances + type: integer + label: Instances + configurable: true + default: 1 + constraints: + min: 0 + max: 1 + + resource_definitions: + - name: ram + type: integer + label: RAM + configurable: true + default: 1024 + constraints: + min: 1024 + + - name: ephemeral_disk + type: integer + label: Ephemeral Disk + configurable: true + default: 4000 + constraints: + min: 2000 + + - name: persistent_disk + type: integer + label: Persistent Disk + configurable: false + default: 4000 + constraints: + min: 2000 + + - name: cpu + type: integer + label: CPU + configurable: true + default: 1 + constraints: + min: 1 + +runtime_configs: [] +property_blueprints: [] +form_types: [] diff --git a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/icon.png b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3d878e52682b6a7d47499f1d3414c69cec00ea45 GIT binary patch literal 62860 zcmce-g;yLw(>J=y;_en?aSQISz~YO$y9E#Kwz#_{xVuY$V8I~-Cj<#WgCsZvCzm|$ z^S$4_|G=F&eY&RRS2iaHu?&0R8#=0FElCu35X8>{e!&h03bO4=|408pa^{RzqBTh`M($h z03g~PfcRgG;YHqeiBH;hkScLF@(7@M4p#P;M|A{Hy8k4?sXs$2=4*&pz z;9mm*-sinAfd=;4`kwkK$|6=S&KwrjE|xYNzRs@yqyP|Kkr&e0#?u1q>+JN_L&R5% z_CE-b7y2KYlNS6Rh^M0%t-gvnSlY$i2F%aF$H7G_jt&NcA@0_1L^NgO{ww}+Bt~oJ z>FFxM$?4_Z+<d&iJPMiGFkd3asIHZK8}nJMM+^$H>AL6Axv;Ylt+9@js1QqYCGBpF{ze z_)EnRJ*tE`#ZlUNTv|By$nEt&l1ek zEBPr203dAB0pH+si<|db&Lk&rJ@a@dd?AO;k5<2%Q*O{KLEa>RBF8B*Lqtu$Uhf+A zKj+i#KYKbxgrHOp`?zs7V}LW+qxUBjo7m+>Mn;Nllc|c8_NQq$IXUZ$`o-%Edsi!> zyAax06-*RjqJ{K&{u&Q>#Rr3QA(w%N0@Br{|Dw1icIbVS^$QO;1MLrcI>ted+!cgK z(5<6x<8S%rLVCeSP+=ODB^h{S>p*qDhquL6(b&Z*xdMzDU1s#PJ{x{aQ||<4sJhxl zE)lnZMY2>dr+3g_R^5G!DCZEC)s?lW!i?%r5mkkEuWfkd zJpyN;RLPE_R0c}wm&#bPtbUi_C*dMStQ`7=sZ!ml&@#*=^t*BT6@PvJPUmi_lmDX0 zDX?0}YlOM=LmokXE?2|ni+y=xAbxfkAYBG388^s%%*M)Ev!F83sH99KPA+^3V9z*x zCFqPEPt4h|?03-NId)0E1z^vmWqtH;FPuHqA?Eti^OlM{B&VxP4$B(A#=lxz?jiXR z#A6K1lc`3Kp5|Rl<^1~5V;8{AB*OcQmRy@nj=FuJ4xfgbdlS-M^aG&V|BpSh;oEpG(E)`UsHwLNRX3Aq(F zd88>RrKhX!6O$%mla9c9BGe(adNPFgFXJ;yL*0?^*(RDfLfI-v^Zn5tM$s8cCzmrD zZqBVs!I}r*(X-Gja4|$A@NN45Y#7 z7K{^2fy@Vg5sg0_PV(x?O!yL>UGqw_n}Du)td(r((>L6f0h;PODF`V-@^S3kyo(xR zM?Q-w3zAsP5khGfh+sJ3UK-)R&SoiBad^faT>GQf3vP8Qlh4$*b;Bp^1Pk#|_TA0_-;soKo z@lH7oDUS6s7>LB?xZMI#S^xe57zyL^{JMuh97Sm*RT64b#wruYnzE=_vQpU2H zcZSs=mQUg6Q8!kaMi`aHs9@iX0uv@BWd*b}Pg1KK8>Ld6CI_D8<~J7Io;N-;q!@1~{cGrLH8xO#YR>ZMZY$_$E+c@>BG9 zcQYk&KH*Okx0H*U;1RQ+2CfdY`h-%J;pMk1->ZTQ4byN+et)movy9R_+bcg=0 zZrkqP-hDJUowEhLDKcqGyyBH+DnTZ<{z;H(&NyAh4;A$_8n}&}l7Oatx;whU)||@} zqfLm49Ub{-SfV8Y1kX(?*vh6I;r?hHo1~gYeNIAlI#Ut>5W&6pEW}*cy{_+l^?u^C z?oTyavj|?N(f?TF9@r~tmwIlPJI!V{|LylCjz^i&^lOR)yapT@$pq%;?HTF3Q zgG)#rJ>`8#wmAEzLM8Q$;9!-6ohExjOL;_fp*t|Trl+NdV;XjV%z7*iD(I{kef(N~ z8#qQCq(?=ve;Fu2*H&1K@<|goX7AR2f?|SpyhLw87z#+J8qBjjC>C+_YV4iD zUGI5uAY$ug4u4C%#)@b2oMAMSnwh-+hf$ouJd(8Y+gJL7AnI)oaMD%F( zl57HF0SaQj^OHRBe=?;Cnb}O6a~@$U73P$mdH3breE=N?09k$}e4=!C@ofS$?r=l) zh_F)5?1&1(X*DKk&fJKGXtw&mGZq|~DI9R!iNNeBbE6bJ%y5{Z*y2X}dYCYUNHBRu z+P^1OFGMX^Y^HA04MvT(Oq5x=Ak1n^-4&{K*9hh{Y_lQ*i5NJ-t@Y4;Bf zl)krOA+jW#8UJGVWe(2}F|v%dwBpw4m629OK}}u^9S#w^M&Ie2Krr8%N8Osxv+OD0 zFh>VB|2Q@G>#CpUrd(^aRr^-yNSiP0Y-r92w$Hb7H%Bbu^3w0Okk;>jF!J%YP&dB} z3Vx66llOimA0{5|`!U!FJD#GE{zMd@NMq2Nu5m+kxo%xT4PA0H~DPKcA$#9+TtMsOkY+IAd z8a5a+nTF_LD5qsiA}OO-ivz$GswtQ`M@j*!i`4>N41GIae1h!h#Vav0uq-9E*W!r{ z$e1h9bL^ZH0~WmDX`n@iB^=_5EM5DHYq^ z-9<}S5(b?F=TpB5 zM39|Ul4H;iht1&%h7b1%w(|$q;q{;l z(Ex!w#snYiFjEmdvkD67e7cQejWd#7mF4&YHIK{J+@w{rkq@j-V0`n%C4P2S;^?@; zcwTVuJhr{A-D;csn#s3|L1Asr-xav#V;jYsfsMV*?fWw30|ADUpI1BA zoBJak6qoB2$MR+Q&ixhJISkRwhmPkxHf8lYc4e66{PhlT>>*cEka=JwEll` zXc3IPlajW56;I@zOzHOTRZ#@QCe^L}xi{n(a}O}@WL%w7)~x(#6p%MQF$;om)*alVc|663#Z5p4d}b;IvoM2+3IfjOjtogW4Tg4bNyG8OKVC*W^>?Yp5z* z4Y2j5Zhk^qfOHIh^HfVCOu(7Ld;hUD9fiUVP?l1Xv)ycrW6m!F!-At_m6(1ssQh`;-j`_CfniJaon0#TUTrimFuZk%lD z*JL9eCpQ}FWKnEB*+^?4-H7LmBs$n$)g4Z&x-EwQ{X5J`1t$7pW!b$O8~7euF|Dw1 zod)Tmfog(=`+eJkNmxxz^hwb~4vHkE7_3m_0V#oZ$GFl*OZJ zUSx4?RB@HZ?$sm5d#tXqjh+O&c+um(jK_iYW;NeXfBKhMmw2mXrn(%vB`x~7_tM&o zN=!-l+7^ue>Ryv^tqLFjadl*o!X^-b_K|a>2rvqvG_No_$o2OC`K{jla9dAYlfo=&f=5sJ3xHP?6|T0!7yG; zKPkHObo})RMOW1w@EOQDC^~!i2Nq@lq#`B!IV^m41(-efUaQB<&R%ch`iO^IlYA&v z5Y;gJNuelicza85ffyQh%ns8yC_kdh*zUM9SkSyu2UN4&h$YVjZjXtWKu)hma!+RL z2_}2pc1D}jsjc<|ufulv9d=>4jGA&sRegWxAg2J1tFXN1fuc{m@cz(Kx)M)Yyh`KX zmE>EDd3>7CPv&QDqO?JW*!tL-F&x+;8%>a%m@M8A*zXllt$xuvB@+w$9AL`ek`h;* zl001F<;8P$P#U+`d`C=$iM{jZcx@T_*$X^8MbW?uKPQ@7(PQQ)bl*o+6d>EV5&RG?wKxd~$p+DS7BKvNFB}_zwJHG5GHL{SyYv zS!F$0-_fZ?=u|H^P<8o>I~Vo}rB%NVJynR#Z;3~v{ zPPX(T{oXwYC*T`VRNX1B?ty1u&Da%Cj{)$G*QEf6hWQJ=MCw7EWk)A5o^FuoD4U%6 zk><1e#n?~VA9~}cYPR$)07AV=>-^-KIt0y*Dl;4p9#fXe_KyR3ItvCaefGANa~rUF zo2UE8P!RIs(Pi3F?lOiOjlJX=f6*Dg!?xYOa6)9+mBLMB)Q9FkL z3G+xzFQ0M|0A?5c_wiA?SD?OjpN<^6uwZ$$-C<4(<1|IXH=Xcm;(T&BM0g2GXLHFu zc(0ORbLJgI{#c;%8r0g#%H6=g;9#XR`Y?ms(#g*yBkvAai{#R8(irjpMFd9%rYpy~W+08-PwyW3G^T~S&@7aVArLS`YcsAuw?4JTp)x))l39FFh-ZR65)!UanoXWo8^GYj+)7S<_ z_}QsbEJ$s`SXDl$1GL#;3XAdyJ_tpwp2h$G<24@ufC3~<+xK*wo&7YkW8dJ&=R(EA zi1Mbh8gidSvY{a`L)|}7xS#(63(w-PDI@;j2H%g8dk?vcF@f3z-E)eV6jjJQ%2|;v z3P-S@NM*ConlKSVfv9K*f~fEaM;5BcrD&BF?J^sYw&qZ2K0TwpKEZ4U&3|QcpAGPq zu++4t(`BmvLn2HLx1g_!)6Q57NPEaLzX<`^A#i zsj=|J0-Qy1>Vm6Kj)>I_OF&Oe3zlbtEtK}&i!pc|Evp#hDc#(+Id1x@KjF!d*IYgej!^%MDY}ka^g6Sd8!<{x&4}=4IR4SjI>rmf%x54 zU$vi;;(BcGM*PC3VUbNiFO`QuUgjC$H^|}^vjgp9$iK2{gW=6<5P{n28hRDTBo z4Hw6FQcqw^q3EyZa_!kxtYh8x8e$jh2YrvJrDozS$2X^8R6lnf2J6*oa7hSBb2!kI z7e@2rT9CgE_i=(E?hjK5<(?=^39>M7d>twhiecM&Mf*q(dOaM{(agtW?k8RX9cdlC zIM{mcetp43IjLTUT&GGJv-p)wubyYWHX627>9C6G%a7C+lX?qaUv(z=A)j%Qo(a3f zHQ#+ng_?T@|0r+zE&r&qZnh0Aq`#dwXBNn|durj*1n81_^cDRY+mo9tt_`!3oQ+~b zmFjzU&mc=c3X~s`53PCm8U~izo2|$L84u`%EC!w!dC+HarMRg5vBX19-0&onvC;+k z3o6QpuBCgJXx~<_w*@!k71%#L!*EZ&uOClSJoeqT+UwwJJzW@iapBZn6|9Y zy%iT6<=s}>mEA)Oz6l=iJX0Lu)qgIS6|0QxY%5<&&(;Jsjf2dWOcJ9sn{)HR39xp- zvf&I!ETjYP08z!wZp=v;Tf1#P@A@qMe6ZV9hM>nI*r;9Eub?MD`EW#qwF6ixsv>6#eM~ub^Z5NuBg)K$Wy-rl&62I#MNbrS*%L-@h-YzH66$K zBbcYiafezU&C&lV1+d#X`9|5>oNn|=mwOs06Vbc@ph1*yaYms(6dt|mjSC*dN`JtK zp2#JbC|%1D%u}OTb#CQ7^XXB9)klK)&q%lip!o2j=7;g-uyD^EFj7%ItOn^ddr&$x z=(U3PA+rANtLhAu8M2ywWD4kd9^x#5{^BjN;p@$!w*%DJ1$OA4Di4}-hz9!WP%je% zXs}P{-0)bQhEZ;Ly+ZmZS_%8Uf%Lf>C7P!i5e?H`Zco*sBL6V13Acf{_(@{%+CADE zsOL%LAys065VmhoXF`~ES?aIk-$ENHjBl{M9iK%EV%5~XLS=YnE5u;So0Ub1g}jIT5Z8pTAgBtJSmmi&!Vgv3m#ef#V# z7(@Mm^^4%n%%K@mVp}FUd<0f>UvE%6`J8hS)K5LIR8+?B_bdDqm07?l5MhR^6Wnig4^K$Oo~}Zq^9Jx%w5! z&7~$S`Ck=TYKell!@;Ncx9G^W8J{@f?;vK_)HD(>qmR4b;Wrg2_A1o~`r3a?bxK)YUuPTaaYw}~>2saLs($Q3 zml6loBs|v~@*g>?e5bJ~P8jPXGzZ=a9#Fx7v+IqQj?&cw2e+Uc(!f}fvDB#?34r+- zg@0?rZA!%fCx8I`4u{#r(R0Q?5Mt%UT`$Q-1W@v_8$g8CYa#upjoYYmG&2b}Ae-T4 z9B~UloSU4683=kCKv`rXj50%eTHF2G8ML$HLfVKou4wq&k+K)kz00;b=z z`hw%Y+AG&j-dN?LZ?M1puxNoVB8Bjbgj1)(m3w3T)_u+WV+*ou^QI*s!-d0tKBM@Q z)i+%HmF#NRo*=neyP*qQp(X(%VDnn?pTpj<2SP<2f+9C8kv~SSz>xetX zuI>a-w{E&Jzb(^YT>%NfkXhqTHQq+bLn5hVVgtz2YxBZNz~RzGSsEjT`@mBO)Nrev zy5E)7fjb8zB`4kABD~6pz5Qm^AuZnr9gdCI(=_uabDMhSXbHVQH7}Po&DAd>zDAZ( z_c@~gOFeiX;s%CtV2H8F>$HX)_*YeY<$s2}^~JKUt(?vudkk%K}n0nH0bimSqOcT0X_sH0p=s`!XyAIts zCoG;fpAy8gFEZlbdzn8yoC=>Q5j%HJG>4osm^w~8Q!hsWov!V>n3A@ht~`5?0R}g6 zXsn`fJ23eos`JTQEGO=V7#~9SNnJIjU&aL02cOf|#?=_`9-0ygjpY=x4Lldxat9Oo zpO?PDT5^C05u4290Q%RSs;{S;!u0FxO#-H*152VFE)b0lWfr<^o{<8mh9<@dbqn-@I@#*Lq!v@`o;#cxf)6}Wup zL?=ai%d4pg$fJ_&1Lf7U-N2l9AFi2~lwr!IP`RdGi!Z4OP;q<5pa$3P9O55zmb)Yg zV0*tj++CD@Hf7SLtUCx>FNne+E*s%Z{?Rxmq*@U$DIcT}+!67~3MPeV3HkAwlH?j| zb2HjjUW@4@yQAp%mXFK}lS@impK+08N-gB#aC0xtwrbl~b4GpsA;#+}9~x>Drdi3v zU*A?>)_`pt>A>7^{g`fS4NoTkpCQ`qR>hsVJh$_bJ|T~1LHJLE#7CdRLO%)nLTq1JfSd5s< zO?Gi&$uWmu{NdnUxC@_ZI5z5&i*nmV<$0}-gDr8#AMhxz!+BDaON=?P$QI+u`tA9L zm_0fpKojINn1K^p1b|l*ZdFK^bcjlP2XRy>0hKOTxI(j~O|)z|4n@W_8gCQRe8l_Z zew5#f?O7ym=8CWtU@cV9*^#U!r>o34OO9y?%nyhZfDoF|!T}unE&=K{D9|vn9Bio{ zgnt8Byp1s4j7624!`?rnO~d!RB+1k%EV)qHNzy&?et&V-*etOCbPff;sh}I&2M4$h z_(7mn5J|%Brg_0tv@I8!f9&;YGtL}P{)kUegbI*GItt<6O6KL*62tDlNa~#6q~lLW z4$IbI(Jk$vh@HSKz?%M+B-xHxWUE@h(O-J=EWWaIY12Hs6Somyoab|@Y1HEDD`))r zyH&zOTu$hF5kdGHd}Cvg%d>*9;oAL)r}jUYkEtQZSlA#$^}JI%rN2Bg=ey~AoATRM z=tn;QfHvQkVx@IAyVy)t;qq?7a&HI(E}qU)q%&5opqTn5`5-ibFb4w7AI5|k7iRn# z1L4u34)sJO zyx0CRpis(Ocx$;gF|>(8OR;x$*5fnwAoCX@s$pGTtenAA5VQZQsdi&4+gsot@G#0_*MO4h6s2O5VhxNHo;?U^LV#MtgnL{i1Nm zltegE6AjW6+UjfK2_<1X&=#`Dx^(8S$l!ek^O(t1fzNF;I%=i%uuQ14y zflsxQGa(0xA=`en^Ani*rp+9(G$&GdPX6HyozD>5yN}4BpG8sfQDWnF+<_b-K--{8 z4PZ>`b~GlH(@ktho(9@ZK2eGk&08%@rFwLX!-Q~*J;ssbz@9=Nxp#Y5& zI6CRZ-wleEsmyIFOYK{V@TmV_P`0M0wJfu$86$-EzER~OJBJQ$Sv&=`IA%zms|ndo z%KU|ey+$TPO@W~(gxkW%3PL9LZ0I28AC(d~?{nL}pRZqT?*V3~{wRS}@@cA9W0JL5 z@+m%_UE}$AoT(;c|JdbBRYr{IzZ5AkzC{@r*UL!Ba0Dvf%PYTL@6QJQotTdsT}Zph zK)SeaV`w}OP3#{ws$vdw9pUs+5W@M~iX0=Z&fM=ZanA46c#sgW1TD|6($nsnndk-1 zx|x*j7iRt`!t*U@U#Akh8-G{Nk|6W` zc8t@WAxaV_Y`##3=}O=&`lcp|g&QD?w<&wSi7g@MTli=Qu8UR-WT%Q`5-zO<>%Co! zyqCiJaw=1TjcI*NTQjC~Me?NLznk2&@wQBDO*Zy^C@<1JDBy@&eDCJbA2P?q?ss|^ zU~rpA-!6|Yl17L;#k`Vl=aa|Uqv3%c37qJaa>^2&sKUUi>3y#d>1U{Foza8%h(d0GWT-UsGfWccwcQHml>T^R7Tmv zfUnxjSA{anpg#7hqLuu4;XZMA$pE{=!eq7P!%5ydcA3Xy-du?xwV>U6fP@AZ(JrU^ z+c@Wd#S#8-6;-8K`iNSfCxpI9o3RnppQbk%srlYYC+EHhWPKlQ4-c^!{k(gf|80T1 z=GCCS|6+jDcLGiUzu#f5XM-*TfKRQS%6gnAcgST6rh!&A-N!+StTeYP(yff6lFISk`LiY~RAu zNS^CGZ=x4M@5K+VK1-ixQ!0mYjGG}ILB+rdSLR`7*0RoGWYUFjYBvVV^QJ{Jv*0Au z3@Mx{HnyNLY-9X*gdFN@$}b{m(a|>>hGvwD9SV>zX0G#wIx~q}5uT?C74Rb;iUCz7 zvY*vtoOSHhez(^zr=#EYorHV+zgXfgYXagC2xq)m)|G{!M+#lXkOYEYf}w|xx?PON zPW5T&i%J>Y7TpIP{_yyqxH?vMDiRs1?#0Y;)StzD5*if=-pG$2b>-^=%(oK59;7W) z6}w*PF=xy_-icGW7^^5YAwm31+#>;8Wq3|2IRZ4H#iGiuIVm>=SrgQx*~9#wH%Vqe zG7yf{aUW^*#-9EB{zfL&fCTE9);+p{5PBxe$mU#26eP9=Isd{+$dIQBKz^2BKhQ~I z`0sDsRZiAMoSKG$+14+q*=G+R#$)c~=|%cKcM4Swwa3!r93x`0BK=FAJ1^FO zQ$%-lvn54I_|GPm=ORo_L0%{3C|n}VZUyPkcP;YH`!QoX+vs?Wq6NS(SPYzy5f!GR zD&DD{B!`;viQq}3CYhErZP8Kj(jxQJ?a6dzNN6jY3v-$UMe`i8x@V2@dQyHSIV&Cc z(_E*#bONLC4hPksz0xkeaoJKV2#2)CgR zd+4JT13geA!>7P1ktuUX_Ol$N|I;M}8nPDpLWn}_49Q3CcGp2pTb;QaIO1_}lu(I9 zEJiCXvB=qu2*c5b#=gu62RF}5yuZU(ZZ6=Fqvi`ZTA0hsmRJvsnl~n{o8SP-S@R46 z{xOOVYyL9PAGJ78ilM@~8$wwi@*Fe1f6>=I#$)69{OTQLiGbN6Nq~}mU!XA7Rw7xV zfa72UTG#!gN)jeY?T_FG=T4$$C-k}5;nPv+-jRsE6+a4NQN$72%sUPa#xs(MWGV;} z3Ye8j6*rJ`cngM`%?_BEN+Xu^6V=}`L5D)ay)7mq#lAJ%E~Xyl5Tp@qV%y!Cr+*V4 z{=zE?&~R%7Z$>o4}LL_Vr4-fn1!78XLm^;2jMhvShC~Qp z?FIv=H%Z_%IJoR#q-OYJ)EV#_hlhswy>RxBg-fQ4jRk)aQ3ftz+>kRCi+BA3nf-hG z*Hs;})!FT(lNt^56|X51-jvGO+uOUe|DwkV3{-AW=5Fq;q&(o~>k$9;zFqbQwgCs# zJGQ8I>_t40qX}fZ?&Z5vsn*2H?yTBF!*)mn3HUqx6>Os_ToFsP^EF6pe~4SM6K%OV zczODb(qOsv=!|&l{mU$dC~Zi9pT|+blbJvAOo%e`s-){~Qlnz%nkoH$&XZ}$4vfd{ zAK<3e_-&waE)MSq$elkPO`c;Pls(tn7A))e6UX?N95JzUl;9yhClpt_ozFMy_>c!* zoj;fKqu7KkmY4Uc>VP%~9a^S|cr#)X>qc5ytk6~6i2o*PoN+$a&F0$h^Dn76f_rRt zMiX7md+O@ynqud(Kws^+?_Z%$o*E8>aa-vAsdZb&q~q(i7!V>zGmgi{ES!FW#njS5 zfe8!$<8~%>FKLL-lalgiZSc5~VG5T2hyaN_R?Hg?CmazCCNQJZ>O1maavYoy@T&jhLy}nTJ?~Nc+c<>QESLom8(YA>k!!5&v1HgeI7sZ~q^n@eWP!Ny=!aI&G zi!5dxvwZmEdn@VS^_2c_^DTA;$abFpFn5hB#L_v%Kp837UBk*eF0ojlGDj7p%lt73U1;N6PHba1^u@W@$-(e+=i>!FPP{l!2)9<_5x!PCWF|DV)0UEPtL@^Gf3pM^KwCx z!}p(O>$R6J3pMnZvjq6*qOWUKTN@!p^{mDda@K#_$)8SlgsX7c)Zetwdu9zn2yQT} z^n33!aEGL(XWU1V9~?6={LK9a=SA~5rRD!sDzJ@>{YEqJ);O@;ubidY`XAI_NL0Wo z>{seYx}1vlCiB%=(P$2(TiYxP(sPP_eunk*;!mwC;q+|tS=9EBmKN5Unid?LMHS?$ z`{SddI<=!R4f>FI8Wm$hEkk#wz{M(Nmcu80ZX_mJT?h1>R?oYmI;}BG9KoQg60Xh= zY88y9gHIG^=gP;s8`M$VQ=-b9Ks^g6B*?&?qr=@)bA>{+vHEMH#y zDg5BRhfSC_0sRd81>v4fKc|OHrp@tc)%@XPYgNnU_3sdv3mqq6%f#}m`a1n)wfqzi zg`b}}a_7?T49+S`a=fE6g<+3B^qzz;1dE&8zGOqx$S`H{EQgvLe|$PS$zq=#MD!5& zNwsaF(zwyARU-|kPAhKuucTM@Lj{U1+Bm9Q0 z`<~e@tAN)!m)*R&>8Vp*@>YQ6`&0EiId< zt+mclaX;(hVU7wtTo)7H(IGH?+G1kB3s)JFjAS%)imGj%`r=bHiv9X+y|zaplhSwh z3n%<0TCUm^q>Ib8^>#o~63r1S%aIyFt-`&DNlqb2$M5yr-hj1_7L&tTgE}P2SXNOu zvpxu`=#On`8W1IN0ajL?`+?cea*N68(m|qqH7s5;f40CITO4EsBfsM{LW)(rU}iI| z(TDOh{gOkPKL++#hJE-I?q%YKJ7sqs`_uL6dpH`R-y7!Wk{QxWPW#fYlb22KqHsSFdqW4mdx?Js>E(1tRJWt~`ky@_s{$7=M#tx6>0=1QOv07K3Wrr!62m8@@f zL>i)ImzBG;$A!E@68c(h(_TqrK2gA;CVI*y>S-6!3)qtC;I3O~5uM86qx}_jnC^tg z$_nu}2S}SYfDq~h3{R*<-HW&frbvEZmiK-?1)t9NzXF%R0Rk9CEkI7({t-g z)9-QRKOCjZp#n{2E?65utyF=J24rj`j_0BeMTo6^$7>`m8Npaa($ubWD@w^m!$Y3M zuMbmkt7Q!qtT1U13NODDh0S1Dl~PbSMn(8bLX-dZp`UZK$q**5+a4c%W& zX-pXHmgTQZaE*T#-dgo`A$lII$?2YfZNuB)=F=HKgn3p$GliU$k&%vd0pH;Ra_mil zJ~J0anIQa~Il@4V@xXFL6<6T>`_EDz3d9Ci>p!~lAy9>pwY0eRHPXp+AEui=066e9 zzEPbIA8G0YrWi>vSvC3Q5cjL=0)F{Quqs&9VtI ztF@-%P!xMLuA1iQ|E3DS`R!)GwdM-1#dIz%}Epn z^Idw4R1#S{p6Yqc4!?Mx*DAue)N1hVqa{w3?-AS2gU|gF zy@Sk~%{-{6_(fr26>a!@k4(-y^?v=auUA%dD7DLIoq}#M^dhKC;+$FK$4W!(&+a2k zZex%m?G~4Q2}ajSTwk9J?94=~82MCzhQQns2^BG4rBVK174~i&HMvg2y-tkcnQoo? z1!7?64Xzk^Z9DlxMKRl72F=BfKA4I0o4jH}wbU+>O;K>W$garSki>M}cFOGVFI39-Fi;BdA08Kx)@mSTA}|+V)eJw+>?HQgIhX)=yQ5 zAg3=&Bn8!!Hwrf*K%9HfZ-&mU} zVx-%`P>;o2nK9s6S&`;6cHVGR_G9K_-42^swl#cU_ucT zF4T<}({>aL5u)N?ni`_jcyP2*&l#5yV5-NXyYf%0v3)%LMCk z-iA{gqFjO^OhMH9I5SqNS;SH~?*lRk-S3T7iQnHt24=AmE?d_h|a6;5Y64ib*052dV(k99<(YM`$1&7a<~+KI+JouyH`4_q*8KC6(zp6X-Db$nVNw+C1HpC*mLD z*%B+^}Roo%$aTotiMt-k*zZN0l=YY1{v&vfonjj9bQcywOyd+q0? zr}$N(pOHS^zquLcX?gL9H1WFYeL7CDzuU}Q{On<{IF72fI2zU{Hp)&Cp_P%}L2olh z$g$o&OFJ9QljX)U!gPoF$JL*n)FHi=(<1fUC?P6V8M8#8Ow?U{XQq?>SyT+-ROVq` zf`5yPfUWLo9uub5s<)^igQ{gEI6OEIkXQnZ%os*~LtkCRw+D?fGb+OM6%vMx49IFp z*RRv85Fop*G>dv4DJ668Sa0|+D|rVV&>XLrdiSunzBFb!l{5Gx~_W zfd~n!#1-eUJ-l5dO}kvisUk6E^<@p7;_nox=jX8kEH&|)Zo@&y8WM$Vj3=y0S>4epKNv_r)InzVB$kmux zax(iZJ{CbQYMqjYD4US6CAgwyc6xhb!G0{tCTKMe=-x#0&uU;pKNv$N!9ppu_?e0NuKgLgE_I~tv zn_FD6B4rM`TnU|}@6CvjI7RU7TIDwg`SZ>6kBN}r&S&ZUsD)819I=l+XirM| zo{QXeMjd2U=G#R$1Fx#;jX$zH-sIy%_;fhggcRQmnO3y5`P}ZS8QW?upZ>iH8~!UW z@8xDUl)76q{BBE#NjwG}?$$|-a>UILI^OSvdE*<_2)9u-tGvqmGoideoM&6E`YNkl z-vXb}=Fk1U_$snCTE7No5y-M=h>;UrHA2|qqKeDys)-Bs@Z>+4RKq*p`P_TE`slj$ z;dE5#z)Y9?{{fIdZ@)UWD-2R6gfD*Mr{TH}To}$hW4w5+$jlX#l7Se~+&kNLiGK@i z1$^b2P2u-{;n0N;PLUNm>62eCkG;^J!}vz|2K@F&+GCqfw;nyqP48;Or$gAORU6W+k9Y@z zdB3KN94X3sVwPry35iW@CI`aJ+Z zryoec&GAGzHqArb5IYSagiFqu96l=D=1m{HBwTv#lrVPG;Lu0M88J|*F-=;BFE2W4 zqJCxzyyk|T62f|RYG=lwPd)#7SiW{k7}&38=z?-w>2aWy54Z=F*6zIr!jsR>QrJ0x zBa_b2o>%M-IcWs=L2t%(9j3KRBfYGO#ulFDN-~@I@@#$dwNtg;XRBP#Xt%~A+7p`_ z-)0sqcFNzU6JUjo9Xsa60IP$RX=*;I4xUw=ldsP;?ai7vW{8-pz$7a=zWy6&`-6$! z=nKY;9ArRqP|r4P-4d3)$D@L9ZUl}#5*dv({z4R439fA3e4q;!r|*W$2j z<93tK{^jNmhEII(0)6qOgT8pO-KL3K<0Ii7>BdO6G4fFm#zLpX73D6V#m5QwE*<6u z5a5) z2oD%d)L?kZ(Ej4H)spPiXft#BFhE;q=C0Tr{_@ldrkd#8vx`ju(Z!M%R1#~nGWe~Z z{!wMB2%nS{JiN+FHbFDpGXT}Kji#9JKY1s8rjn>Oo_=X|nEm#0W)syFmDQM6{6c{B zx+HwIm(|U8<8_egm$VvIeQS;?{dx7}RjS-FgdpB&?Dv z!$CE8ar-u5#_40jC^60(a~Fq){`#7>E^G*6PZ?zGv}EPFuygm`@V%cu7<%d}CtW+X z3$y1h)w`N-$Jaj{P9HzQ#BnU)=@^LSkTbo52^|8UfqCmA{32CB4oo~l3P9&26KXpZ zR4Y2QA6IpN!=Jn604}5n^2&-P@wPNY(>$K?5D($CKhMg66wB0a$+BfuBwzSI8sR&H z<~KgZn2CUV_0V91dCRKxTWb~PL&|!G^QKRf1T{!11bzKRd;|d)`(>aBAr;{isr}{; z@uTo&6%bw5nWv2o0~OaF{_;||`++AyPP>j_!jRsQqB@0d{_r>9BUhXoE}St@5?UMa zSB>drpLkxM3aHv2&Ym_wDui}n!{&F)Xw<=&bUJltD;}&0yEFiHleAAgunNBUo$cYZ zx$tj37?%sb>8yYk36rjNSar2aDbj#s@owi-%VPKrb@W@Mn5!40}^4+p;xANg9terOxp~ zx(sYA_z?b;H{aIzYrj(d4utVq2tq2FvtVgBb?oqP{Rhquy?b;GPw7y}AKvpw;804w zMSv8)XvxY3tLN_T{IjIKGULOS-dGSm{~vd9o>rCmLypeJn!A4ehOrGskm4`XO2VQp z^{K2lM`!;+BgQ{yi&l1@k}lyXjYMaNxyB41Xtq|8agL~ZAC-`h05o>Bf9fL_hXJKM z!ml5BB@7?ZGgNl#5Zbpb2ye+8V$#?V;miNc|NU)=v^1FNz=-NWh) z=r9il7ZL`n036c|sUvwu@l3(QbTlVtd}HjmJ_``>RixcAtH(h|U7b31v=5^4;RBZ_ z2n%^!_M-`Z@DIQ;0@Jx20Gbf=n7w$}DI@j7mKV$`SWWeJjYaPM$TI{`VMB!_x|acP zCw`85p`0!SQC6s=XJ-LbVOXjaWvA;ubb;D_xTy~~UK<(eWCtVbLq^OhzF4l&z zOH8e8@zgb~{lq`v03A$lkn|t8vq!i~lGYQ?z8*%89Bg1UOC~mZ_mzipr;Z8tJ^oVY z(W_^eHJ25_*a~E~9^FHC*$EOy%#JQLAa2^W$4o0omUnD7dv*3AOIt^WSMAzYu1c!; zgFTS6rEC4-f80|lUeXS*I7Dy8jF9`>bL;1ol=OL6BhyteUb7yM4-oLb$&HZ7p z-1vx_Sn;v>eU%vefSrMb0-gO0Vqn!vD~TAQ79o`>qCd}k)s9pi zIizn`xNNODxVAqG9;C2Z)>elBsQ5&Adr3st^f1bMrvXp^O53!p5tV5Ik)!7+vF)>UWLYqgQDIn()x%R8gTAW~r zPC1Un?VnDcJlX2b!8aPt?+Fh9ROAmG75cyBD_4d})epo+HVmjV#m0|2uNx#P7Fd?7 z)bv}_LHD23;5AxezSWIu~fC#TRkz4YNV*_){ zp!7gsLFjWg{9Rz+`|*8`2TX6qYo&3;hIb{wKVoJygqb>VSQtNQpz_t-q(A_P=k2B3 zZ?BmE`C(O@Bf*(A@|$lKu!Tp*epgG+m9P5L-nQ)D#`ARaVyqXYm9NoyZRM;SRq{4M zXy^GD@%$VyiO#Fa$yzRD=0EC4jk8W4Cp#(~HKQnDs6 z$i*~p^oT*>TmO1J2l$y`pW7(CDlz`nul`UB*3Cw+-5Lo;jT~v`U6Bf%jPzh^n3&G7 zdNoSc6$UN<0)Eghz;H|rAZK{)dEO9t{KP|iPF)3Do%J~}BosFU_0E~B4m_M+@k3{4 z0f5fVr^!fz{4^!J$J5xA_yAV&2FT3Q{yeC?^1VmkHFnGx`?Nc0CnLgBUpS;Dw$4z; zNLLVp194L~o^P(EXH?kk+Iz}L2rf|#>EBzrqjRjy+3Zf+*dcjdw+icR0v|BQSo_AG z6R-JKnpIMkGf?sj=?BS7-sD(dVOkp7BZ$;ZJj5GYD5?%GYx;KUSAU=q84W#9ZG&x-tz#=Vxl4lUS|BK)LCEWXmzlJd*hm>#Iy}wY| z`0cuN>p!B`uxi6hMkFapWIkQVKub1JKM+HFIzKn3TpK(KH*eZn^WCr9l*OtbJK9}} zi58Mk5{AI`>XzGns-ZeNj2+rrI)t_1qO-?@Td%uPU)pJF?{`YNdT0AC-M<@t`^ZeQ zpzqnOqZFDu!;m3^CE;{4Lr^*pMCYKhu`0!vX#kQ%ix=DGSxhK6vx`x-bLURRfcy6C zw`bldngU*M8x2PXqDRzP+v^#UmQNY!yiiktXO$!!rc}`IVZ#Dn`+*4Wo!=fD!yn2= z92n*TE~^AGwz#0UkQ4#R{{8xevi|yhqx^V%cv2po4htBO&(&+z)P73dL7I17KT1yV z#PY8*&18PkF)Wi!6Es3PNmlztONGEzEb6_ARX#~4^ygR{7;eT^bBT);XQXmA9&g{d zS8lhY?>#1g@6xs}&QfE_$I2ZN4|yhD+8bcPt_uArCxdnre=*%`uhqTo0!mgAk&1qB z&!b`OmYqS1NtXBZn(Ezn)pQ*dGmfUVg&vGn0VjC0gBAb)Na~iALskpt$ZY4vf4w~n z8C0f~K`WC??6W58tghEpI2bkgXrLFVPH8tKj8v#q`o4YTUuxU7=*sen%HG<)Rk3+f ze%@Wbe=2v^3d!zv75)lc6U?9icOZAWHG2(4YWF7z)evrahct+&Ga&N(Lx8aU88?V#!nuo`p9DF(FG@#N3R%kEMchvac` z0+JQPbMj|ErEr9=tsj98Td;a`>k`&T*SKrX0ht!`3m?Dwd^>s=Nef~MRZ*#_WISE+ z3dlfvHRx^C)~>bM8iOJ6^3|KdV!8F}VpB=;bi^OJmFrL`3zAPv`Z>a@J;)D}CkzQ= zhLwqD+lM9V-VMcVV%w!kyS5KM`qdu|6tC1VGz_!=Cz2q**s(aa8Jj_iv$XbzuOE8i zWs6luE+2e%*FK$W5*~G`*Lvws%jRd>`#ssquo zIckSm?8`P}gduNjL3eseVWuZpqT6Va({!c97eM;lpiu_P1AG4gDlF^erhd zg*hFSEg(n(02F}59y2F8F&&Q%yk8?Z=gh-j)(Po|a>j3QmP_P3_eVK>I!N3c(cE2% zdZaJ>kWM1;99lY*pv)VVfYClU9#9fo}ytqEK*1pnWb#f-~tUD&__EQF4FPWgH6JTDXe2s5Ghi# zOxuv|Fjd&F`Q5NkU!q}a%@Z#zlz0_e@Q^$hiWF}bNj9u1vifLJd_3>edsS6z>JI?2 z;h8ezs6KP0@8ma?$>75D5vhzpb*s|D0KJFIY1o%Y{wNpQfY_tR_cJk4z2dy7X4s6r zm57koI?Aj;=d~G#v9kE(|Gg_r7&EML^S0f&Ocj;X*puM{6e@7-V8neoQoILiwWp-_ z*TooL5yKpkNkZO2o#LpohQf94KR@=_FHOZ@V)O@pdG<9aIUmy~J1A%b67$FOPB0cz zg4o7rb>RK`R3}*)&9!Yuqnb^<^aQ;XN_9Y|Lve9fvSdkUBj7@kTA@w}(+(RtH0YD} z_WRTc6HMY_gYD}&l@g-E(%HIc^oQ|^i#g^?cVbeW6E{j@rjSTY>?dEuwfLwDB~sQC(YeZYmJ0$9GiaG|B|E1e?+z|t4i#^rHa0`>fHu;GeS zG-u8n13l{4!E}VCo%(7_tppd1iZsX%We23FSBUVuBVL>n9h~;1vjZX+dqO2L#=Pa4 zE6hfZDO9!U3b3dRtGEb9T1@}nmQM5eSLcO4Y8Qi1eh0--Y&?NK>U00Tynx9vFUzwD3*d9&_X)AK(<;;F=LV;lAHKP)66Y0 zX1?wV|BIqMv?<8fN1v*Sw1N6N>(*`fLOc&?JsqSBF|Ll6CQUE3jZj+J`?p%P`;e5` z2a9!-!=fdtvquaq3%BS{e~wYcT6xaG<>7y1q^J3P&?k^=il#6oP3+yfNA^36bTR6t zJz<4~ZNj>B>uaT=zNMws$&fzi&~$L5k}0R1X45-#R{+(-Q%|+&4jq+g71Oh70ohtj z)i_euC5%$Vw`0dvt(Mj8NA2IgU#;>OsYhqODSu+j8g0&2X*X^a0eR%4pj#bZ{@lE5*JfpzYhY3d~~h5_wnY zoTf~fCMkQc2FenBb5i?4&CH~&)faq086fiOdG>N4Ei!m**sxww=PH5u>@aKAo8~|I zhfUM$HARxN-48WYT6JC>CXO2srcWLn*!F`iEGJufL2cFf(Y=p`mtUV3MhzcSzGKh+ zLVfXKrH+i7EPjNknWxt2Hlhio(R~7Ir%}!i&#E^@woDx!muP>XwnWr)5ECz6`>uAY zR)kwWaZy+ztM(@)U9kx@zaTaO?%1(CY}!;eTAp{_`@)0?r^=0o|HJ#=|9@Jgvo;`b+lm(c=@V%t)A1J#qz~muWOgnYfv;e)v9_>A2V=e%I z)Kw>g!~lcmfGtF4V@l>0?%ufo2c$bd3qW?D0hEy}2_FR*;v%f2sXTIzUDaDiRl0Yh zP5ccTGQ{f2fiM~mU(^<=Gt{9` zW)lvb4~D`pJNdEIK285PA8nR^>dxOhsZY$W3*9xSl=SQt4yep4SFTXmV|u6dQZrn3 z*_C13xbdb6=q1}I^p^mB6iA61;u)aDd+HWfA5oX^3pzRUj>}|2Hd}igpM3H!VfE@5 z&=VG3=+;eNUsAr;Zrl>KOKs7!cdzh)3#QnXGxo=R|L#A8F{6f5Zr#2o7xjjUezoF! zNWnpS^Z-$I(9iUvlLfE@Fcb2y(UNYdpuGIRLmC|~*4BwC*=TA1RLELojh`pw?qIcC zc38fAv2-@Ej{Cm%y)Vp|F(Zu8k~%skIwj@-zyJO3!_7C}Y#oe`4WPH8gVUK|NTw?b z7cC0d4#Avs4muc}kbCcFbF_yINeVp@?^w>Ct;qmNk#q#iI}Bn;`i{`%|T(MSIn z?!EUu<0yUeLU}0FM(-VA&8Bz4xS{>R`=*W!y?S&GE7xoY-@EG(lT`5BLCf519oeC& z2nypEh%kk!V@~o2IdBaaFu-(ffC!zAe4uB;%z-Jamjm4$k``shkAn?iyxagB5*3n_ zr^7U=tG;~Alo^{be&e86{$e1vZ{I!(iZHxjTywd7 z5VwSnlE@F!Xp3I~k>Vgg9cdMFmU|z5CcOCSoX}mynEgw8OG-hqj7fC(z4qE`!qll# zB@GU@w(_YoWhY(^?yk7%ARVW={@Tj!2hg>AF~qmi2v9-vQs>ZUM#`iw9fCAn79 z=$J?@FeIb`a{>CVqs7icawB5Om9Yx z7-8|ikZi;y9VD`I&pkK%;SYZ>p!7KCyo|Je_`^ft_S?T_k54&elqp%?n8gQtt4xV} z!HiQaFGvcQT`Z3;+jkUbb&?U`79B>2#1#XSScmsCT*{%-QI1kgUD*PJUpgbJi_`;> z5>pfMgc80CC49UC5*KyD)Q)q`Irp4$(YXP12z_2}DU+!S2T7jC@|0y$M}XV+{5kmJ zmrf0FC0?d!rP52WJ<6wlpZMVU;T-L?W9I^KGL3_{ha6O}gW|L9PdqovwpQ_O(uC0( zz;eT^H{arC7(M!waP`&i50f_b)vE{Bz;XJAYU^BbYdGf>Z?BVy3}?)o01L6^bWM?$v^Vq8U0v z?x3XTB1u_w^I4BHO|LO-NYl(mv#qayXWnpdKiul zOgQS5sWCb|;-K6(>Oh`q{i0k}ZQ3>(7ERcg3|)QE8R3GnCWT@8PzX9JR@`Dbtr$?@ zhcuZ+ktRD6?!50$Vb+|5VdT*Mp+l#xIt^iC*sy+sZ0ZJu8*ccdbW>AJ8YX{~fjmYD z$&-4(0a#IRp^Q!qft35H?@GNA7mj>*T>M_KVnulI#TUcRfBv)Z$}4}fD9)HR)ds}v z+qYE;U~?rwKC61TM!KtY@{p}HpB#NVc|^ zm3{h@7|#M!WYqnmj4w>I90{<}M6XD#zzq$}3V_EiWuX%i*3(6A!vX}-Cj>A8n(j|t!6*Y9j6QpH>l_xxx6#bdsRvi8tQ=;? zNwfB?J`E)3rbxpHu(Gew=-uf9Xxje%c(0y*w2g?9g&p|et6#lMgX?O2wrhle=W_WPZf>6QZr_Dhs+U*5HAw|sN} zEPWXWYi#V1kUw0% z<(-Ul$*Thlk`DkvUXiBIJ)vK71v>urYC1blE2K!B3;=*CbvSy|C@G1%TNyn6#Dn8& zG+4M3j_IaPw_U;`KS*{+Rpe(-zn+qGDs)idHanl{OP~2j_`s!Sg&{KVjjiIvj`~Rk zR+y*kS9%ZXM2>I$=zcAZ^w8>`PVB7Aw(0go7hM>>{q66B^Upue^34E-^SnA}F=$aP zq$=J)* z>cn%H$O%82wegv7NFAidxW7pN1@mL!T_G?)y1*}h^`Q@a$Rs}pERQP+T=)ez&N$$mWQ0BOo13K-MO~RkK?kYQt1B-4>r$yK(@w`xZ z1)%T#{gc{P_h)@yvNUw*+FgdE+r#o@%fo;E=YMEHE2%M!h(oSqNrjC=UuG_BrVdg-NRCh_#sGsCD+r{qYwD&M_(SFQ$Glqd^!qK9)Joab;1vf0Fyd-r*eOEPV8dntS~XU4kR>ZXu`40rBHt8=r9*yi5H;C zk|Gx{!#P+Gz99k>iJ!PA!xdLtVY;p9(@zgCzw)YdFQ~{LuSA@R&f4{WNzZ|WC&S#@OB8v%z^tYlIAam!Pmu)(xlM-mNN(xv3x`gqm zpc&JuK8b?*A+HYbO#fIl^b;8XKJth(69cN4RE4b+bu_SV&#+?c=CDPlxxWA6Gr|?; zPt(Z;y;N@WUa^%q(vE4l$Y~uh&BZParTm>b0pXD+UbIs|+Z1=vv^J(#zwf^LR9{o9 z9(`fJ+m^N_f9%JM@&M&Y1jf2lTlS88zyq|M3`4D+wMuTQ%D?>OFKv(>Hf(5N@7^U9 z8#b)V)@MAnl>Ej)vHgQ zz$&Xuc2+<9;g3zKqVv$Hypief++)be=;Y)K=}d_}1EOQ$(9JOIfr!&OIGsju0X%s) zfs)7L(i*=Zi&yvlo9Uqen8HDrQ#3}z0SHhAt}yBR`HO8elaY0@^jaf_W{1cA`ieST zUih?*@#R2&=JSU&gyn1@AeT^Mb8QY#jUQlOz?sK2BRd4BEVZ6NYY4M7WW7{Y zRyISkhn?vPT4^^W^okasbkeCZFG;!@ppmAcbLTF3^XARZnm+x^@Pi-x$fj2=Re7g_ zA?a`!d9qv$C1XgKlhGBu0QN9+KJ+bEXfusrs>Bom2Jwzbhele1(Fu=&n$epnpr?bP z6kQnkLWjeYgLppr(T|2}uf4XmlQh;jyn~Tj-q`+0fR`}rucRZNrL9YB!hUnX3hC7NN^pDW!46+cA`JPVTu6-oH@~r#$IOHB zkRPUTkau(pN7*~+Qk8=_@kNRJ6cyx|eu{m6r2l28BfD@ud%jXFg2HG;oZ-N?*|_&RwuzVb*1rT^j!TzrSItd;kHB2lH`->5-5CD@1-L zZ|vxAIu%ni0Oip~A2nq-Iw?9VXHM~+X^t!CnMN?>f!UEX=s29nz^BfAw8js=Ne^&C z`anOzv)gaKJ+4cMNlu=ZhOVA(^1{@1#*FD<(V~U2)p;S5_A8SCX`!9Mit>H} z#(;-m*FmFO*N(Pz1!-pH%Ws)(i;pi@eVNj`P70zkP?tzZfD=H4!!Pl11-tp+CP6e){Xj z1;_(JZecrZdzouWb@&3~T?P^2qAU=3=RPWNdwlh!mx|9R6OOz?w9SV<{P&V9-(WQ~ zYrud3SsG}o#GAcU+-cHj{W1<#%(Bu3w9*N%$g5SX=HuEkm~){!*XeM3oZpreeiDHVwe$pJ9p z_+nN7NsFENq`@j6j6#itgOtWB5cAiQ1!fWZ7SiH&c@UY&_ZAL$}2C6 zN#6{8O8aVqY+iWush4c)MnA2P*_2OVJPpEOW-wSvdOm(Y-rth6#fIuG9oyO!g>-D{ z5z7_jA`ImKoV*U`_)_ZHX%(DELA{WUcOWkl{@UsE0S=x6s4!|hMaQaqRbN8BOeYa^ z>Yz>Ks!#MYg!jC9_+(eQC_1(0WV-NQ-@i{Qfn_1Lpsm_%ewa3GYWT)CzG;IPlCkpz z@i^4poRu{=97%f2fN*6}A00EY5;t?aKL_6v5=G!O;+s#UAP2-#}+ z$c;ayX&TvVRLAfV{Rq=W02T0KpW@Gc_A`6#=~=|b@P~3z?nT-vL_VNVBZr5T>o?o+ zBtQAjn`L;(396VX)aj|#&e+O1$77@QASRG z=r8caaFi`Ilpnw3$2*UzU7)H34?XYJxxEZr_gY%S^{=;l$hOk}RM=CY14}%40)O=p z(jF%lELy%ceEhTjBPpskd?Cy5MXLLN(sPDBIx$l# zBiU`*w5?pRV!0vH%$d&_Q=(_0BYEei!O%I$M$0?6YRc zKX9O?MH)MoEL|BsbotrgmXBR3Py6S4?BEHU5`$ zn1XW{vhsH))75zb9>$Ij(h6dg zOdd25U|(hRWzwxa=6x*H%bxOpCCSRR+(^7F*2$tTYkD$Do#2p0#r!2pmW78OemG2; zG|80WbU2940z=Zl04sD>M+;Woa2^kq>G$7%f4JqATl|$>VS033Q3=E)3#Lq_`x3A@ zoud;-Kl|CwniV}^n3j3I-OsUJDI-Msci#CU<@M8H?AXx))puo;zcGC4OE-qIr;N2N zEK%_B^azhD(v)r2?tPk0zN`}f9 zMaTcfw$t$O4i3^gb<8kZ!DKs)a;!LyjupMEY}NVQW6y=#^)1OEgUYmenqVMCbCACj#W z%G@&;ZCk`W_uSK@8ewF+{PN4S(R#XVs%2UT zMdNK!L{0GI085=*fBh%IZ-0BgPC^|S<}O?nh7IUx-&o|UFo{#cXw>AZK;{tWkzUr< zvH$tMe`>G!r1}c`ZXc_LOs}J%V)xeh^#ZAhv+psh@V$SZ)v?v*OdBtQ&@MLpqay)m z$3jdoIq`GuqLtx>fBv?lB^?K?1(5}s3f_9_tu`o77hWHv;b4Wn58g-zqAlRxLxnxsAvoyC7mf*!DEH4xd^6+j?~yBu~h~A0OkrPH|SP1Il}bCh431cii4NB7Q3y)%tv zwXmViZ3SrMKjaw~1uArgs67CB2;Cb4vJ>U2uUtN8bG_}hf49~2(@vXQxop{zTp5-s z&gu_}pC53uA&)cFuE)kPEmI2gQ?yJ@NlBk4#oXtJ*$!w*QnY+|Y{xprmN2Dvd_aIk zLzi+iD}qfe4vpAwC_Mx zxaWUwwyzm;c6md3BjTW(oP@AeA7c2V}5hrz{Ej2R!%!#m!|i){hon?Te->5xA8nacz0wTFtl6J=dA2^$_H!L~(@VdD1~|X~5vKV8ML50$Q|7uU@^)2zKJciKeTA@1tS8+{^#8MW9)NWnRo*}9vLs8g zj{ zy~JNJ$Bq)2YDt!L+rt#{wMW$w(GGygf~%$YOJX(c5C-Y#NZEnsR^ z17*JW;sUqN;E9eeQvfT99y%D)xc~;t+8MB%ZZ*k^5ibB2`EV|jA$~Lr@1RVf(@>^x zZ@7N8j-lQxs{X+bzHiSx|DsKwGQk!uUT)7USYf3l{as2yBI-)gp{$f0U>l+}iJ$(! z-`cnT=XdtVGmCt;>`mKtdj|=1h@>yY{@Pcqz5cG$lA6o>Ij$ekMhhU^s{t$W!JiBO z-J?l>KR>a6Q`)$;*j`?|Olt$*r*(A~`5+eBpX<;9*Aw`HiIH&5za-d4Jm5U>#N+n! zpZ(N+`oF(8VJScKp%2=JKJ;NL(}Yj}rj8GV_QZbolb`&|ql1hcTacx&Gz}64#$rHI9@)K5gQb`=o( zWzsi3^FF)rn%Qz;VDrx?9zFFx-h2GP6MR5l@<1wlZqX|HnYMW!J+jQ2T2kZ#woj24 zzNQ)3C?afl7e_zBzp1J^#8yM}!6aav1P25)wWXk-=UX-Ue?WvkrsV_~yLRnP`QjJ9 z=)O&inDI;pSRvuyJ$^2kk}@3c?`SXWB0`mB6bZ@v?zhgh&;9eerEZpbXW@QQR~@IT z_jPq1z+y6BpEiy7`R^ZB@I9?L993?M7B98WeCGd3Vz|mxsE+T0H*hUUWa)}B!nfE& zFTH504JjXQpZT}X+hA=^U(l;S3%Ezxpwi*?-FxoQgh`#N#ryZ|b=8+u8Q3cE>PKz7 z?z-!J;O-2xXv^>}KzQooLVP`(kIjgfK}*=T{X!$Kc9#aRuDzg z3JUVSE20mPD%hY|!OZt)*6XI5-sBBSV?g|nXauII&%~@?$n$(Z2Kr!Vro*x}a`^BO zwqwtJ?SQ=7=FA)?=|~1w*`C6^$E&luDoA2tcj2j$w5BLz&9K4!ZT9qW_V;pCn0xtj zw_o8?1-Nkk$>e*ycZO?`bcJm&)t0Q>Xg|<4ZR5s_lueH9>i65{KmP@9|9HOO-TX$% zWM&p13sPFhHxZ8TE@dO^&NbIuW$QLn*gfC>p_OV-sjkEBC-;$zR4oN7wu(*b?PaZG zC@C2%aE|P^*l}4ZMhK;6o_SVkVX3<>kSBR}Y=b}pc!G(kU;N^inwS~evS-h3VP@JG zIo|$C`QeL0Uu-i1>PL5{;5N|R<#@&WhYT4sM<*|pA+cFg^d6|{^{>Cd7cFDlj&B4S zfKC}7ec=@uzD7?#8xsCE*Is*#Jvr}bn>A&e?c2S}R%;^$4CSa;;l2}*347O30aBJc z<7Z79XYagauGRovZ4)JR(Ln$!HBP(sIZ>V`bDe4s>bO>s80Ifr>5mZVX3d&)cK6+% z_DPm7dlvEwb%HO>rPLkXe=QS~j8EabC zEYLVuK|%V?ckXFjjS*-NJrsSC4?spDsxE2Kq6L|K`}V8X{qnhK{u@6TfmNGS5IfnO zheQLc&^Bs}lsh15QBiTjiWMtUZ@cXs?x{-$qY>f(i~EqQ0IiPKx|6tx^lm!sU3b0L zi@D((CHwY(KB5?*j0vS`Fx@QmRDCcm!VpI^CkdDBZr4pz?evL1=biGVrpD|1q_T(|6XK9)faS!IL*g4m#~guBgI3(9$t%}`l0h*V zIldA2jnR==punUFLD>W<1au+cgM`|#Qya%(zaXvNxYZ>j>`mc5zmQ-bsj^+MX7HJX z>>H>xaGC)n&6zXD9VmmO;wSaNZw9>cLAqL}t>^yy=f5~mAvtBsA+%Jxf8TlgyRB?c zf4lCo8FtMTmnjm*3#~C;&Wa!=;yA z;x|WrQ91VF;mS1CXL*bV4~h0AS*Y-i8Te?d_x#Jz^uly}RP3cp!VLH$S=Z8i>>K68d*n+>&fS_e9P#&id(YB_tR3y8Z) zB9$knS@_Y#%|XgyQsU~XBPmO3W-~M+56xaD=j)Q#qX{ErB@$qHSv1u;Tu;GWE(1W`uVW+7(RLU+%Xf#eIfB3J_&9N;e4VU1HV>RM;rU1 z9wH@CcIvca>lP3DGorNADtGSCOklNT#59S{>Jk8mYqI^e_J{DEj?cvN#dz&>bmf(E zq!zAEl~j6EeK0pQwPkMXnYK>XF_UxrJ7EfMhb3C*%uIU} z;lVVGKhXy)cq6`3!VB>OS)$aVV>trPh5?riB|;lepLBs!o#MOI)I_0u!c1q!eE3aX z%piuDLVWMSdH>SIM-Tv5437U+NYZYUXUYw+-)*BoxS^Yli3C`I1lK^UNFO(?k(%zI zh&wupax=s}(QY##)A(axl=yQO`uEVG5&y$X)G=HTlr~CoA%dy{e*zMMaf9?7)9W5`ooD5G{9a*lvTL| z$rr24AL&S&j`@&(@Lkh}6@FPU+tT0@QFeA|X=x!}^p_CBS)u?I#AqTOkJ@M^#!`vD zUN$_L87;A2h0#FwI+J2(=NhRiri3YI5T1`$9l|*p^tF(nPpIp#VbZ#lJcP4moR+Pv z@b!Wi-V-fRZc4RVYX>DqSW#iVwzog5efVy2buiQ^l$-X3PlD7E&L`qA@GM5}^77#_ zfmFJrbgE?yZ5!%*`|a=Xr8ndg-f_G<18|NQF;bITqiox@?WvO1kh%LyP>4`0TB>ur zY|hEGL;)->henu@BHl2qX_DjH_SE9y!q!nV5EZJ>2>fOg=&C`u9!3sCX$;OBxu3sq z4(5jGMlN#Q{v-&@q6AJZ|5Ln?x?XD_vuFfl*MXFYaH)*x_L=h=P zZVfp3rp=mli7#diz|*ljVML+P1JSt$CWhF65{bA~8#iwBxK;bK{)#@yk_2pHtid97 z7AZXEKQ7=8mM~49K0~{yEL0FYM1QjbRW+@V6%wWDKqpck>T1RI-CisL6OxECh9r&} z72o#o0+M%V`}k}AU;=$mHh7z|-getR?&{TR>?>dS z4{cXJ!)tl6b*Hb85B+Jvgz^4tLqntbAsg2o^~|)NLPDq_2?1DI3l(&c$q=te&;`|o z4lHH`=Q8+iZ}cPOvi%i2;ZD(B`yv zNc;|F0=|bjhwF)U1Qg&o0C?WKc{XRx9G4E+NJBf^sQhiqXxf(@O(BvdiBR#r?~0-p%5#_A_HK?j1+-3nX@0ayXOMYmBfpMsWo zuVFPgpv2WWcN(3(>K)-i1+73sXk1b<$Z3OwLfW{$$VUV?tRGymVy#V4eEP*p zmMKpCq}Fw#w0nG_j$t5+KcC3o1K<)un{DNS=lRf~gI$V@+O1>5Q12AY3Lf%p-2kia zeCIo@3&80gr;-94gX`6f9hE7gMwOGrpwiO*B^4FBD)c=GSRw)~;o^^oHjKs#5?6fu zqR^-`9)N;65E>;OtR8=y2ESHWwxyjrDzzO|Z|kX1D}W#Q@RysLC;fba{Zz{!NZ#h$>?gy)*O+0#N%X(5-S(*D-QJ0R1 zHib9Nw-k7oO-k5Wj-@(Z{p#0Fsu}>*yWaIK=i4ADoz#P!+(cB1*|TTKtNwYB()&ox z(W7aIT@~1|BwTdz)VVv=CZe3XcXoUesNKZY^I8wq2VknLt@hyr=QmP7fBzBt9hfpS zG}j(@-~m_d_|;44pjK?TD$=TRYed2chsHo(gsWjkW!@{krd3) z)xM7H!|#rgw?A+md{9>s?Soo|w14~UcSzNHNinWgXv_Hu-y#maWFTk#JaZO>Tqnr5AO(_uo%Uen#Lm#!7tluee zX{ZxZ_VG~^ChV9v7q1R-ogI(g^rkl{%*X)w>P)b&e)Ap$k{fP~x^~r^%Wc${F?QqJ znB)a>wB{Q*+_4>3ks@S%@{=D|7?vn%W5@SiNWa4@+RmMOZTP5Bwp05BvbdO?XtXXw z&EU<_?Nw%@jQv%SgcW!Ju+7Yj_MeO|AlK=zq0_3T#a})zz5^UVN*W`cUPA8n=UO@17yZ{ETyz` zSgY6{8YZ4D0kF8n=A`d<$E`M6jVs;TZhQ9~RxYrrZfJ3XE%x@yue`?Y(ms8gH*eAC zob3%9@*}T(`}VkMsO^q4+rk1{`t13~@S#p8I>PYfqegnnr3{5HIaF8glGTZxJ6G4; zLL>5fXFXEyuLZnFQD^NuYzkm?zPB570Du54{s1Co0a>t))`2mcI)>|ff3;vm9=wxd z*a8jOldn!i22ZDc_kMIcU}@9{sNjLvMKe!$?P5F)9`EsF>exWU656_Thi@IveMPd* z;1|Ib@_GA@j^^JU&|J59MANi7k zxq#oC*MGOo$hH2;^Ez;)An_D}FpSXZNLPsXx z9ZrfQVolZ89FFST*8dZt)(j#?G~kc|U?E}A{-G}M1r2-=f9`z$4sTMg08o67`pVg< z8G{*#wT;m+rNF;LE)CtvA?qL)Q%@-z$p}dj(TLr?o{>n*IqU^tv}u3O zh51YULFrgRsf751Q7k0vV@Da-+j!x4pcKy=)GC=yodSfHA;tB9C@P>Z?9_8Rvri;~ z(cNp*(xL$KKn%YSe9^((002M$NkllFbN zBPkV2kA;{?tV;TT6^68vC|Z2|PU80DEpZeG!a;Dn1+Zea;>e#Q0xX1_w$2jw{JHp= zFn}eMP=s*K^bf%6cqtG!_Ke>O$Q{z|A%lmOX-1{d?*G-jtyzp6H`Wd-tZYXKE!;q- z4I?);k_(;jS$~E)O(ND*USUr$rwo1F#)_dO+K!?80+{JG@!Q=`D~282_~>CE5V?SLgTj}4@$+G5Bn z=;J6I>UaPX^lPSIXDXEJ!i6sxVLIyS4ogx>bJg$25k(+~0o;iUm8{tcU{+pU-b(3k zuJyzP@&(m+)5gt~B@Sq8JZ3%fB13k<;8jeTg8pH|58K?cX+z+RjsO+-;zDh=bHRRE z8t1PG8ixjp&Wyh2qA0I;N5Y7D@}@l$F`qb8>G0vYE}4@10%PKtd+V)l*Y7ASEUF;P z%3lDfHp0K6{(b`HM^eKEMjAPCq#rwW20wX$d>o1SHudY@&-Tfad|)3rW6B@P>l`&f zLe!L^WNQyZ5`zkf!Ho8&jpN&h-?U{iogM|CA1Uw#{U%1=VEE;RpsD-4=lY}D0ZRrx zCxFDzgH%37GnbTtn%5aoxE4~_3=$^BQ#XKFxPH7NjfAwJO(eC3Ym+wRyxVt|Io>np z>ou4!fcO6W`@I^hDLh!Y+xiq2NF^3v>0iXQiBU_!%jD;^n?Sazsw$!gwALk*GyvAA zw10Rud2FLV2;mcWMQjr-HHa2Jx82wc-?|;J1TYR*DJd=c)uC9%Mrzr$tJ2*I0@31W z17pIlA+dbni(mYr!pWc(j?{C9{8AV(!hIc&fx!z33W*Tmms#Euob@{1eah+!w}=pX5%UfKb#3uap@ASh4wJS=lfLyU%^@vkF}@+4BqU@gEn+N1zoQ zdfT?GUaJs_rN2}{97w}G-CgDQ7R$}awygZZRtM6p+D!&uxsHQbXw&$?th?)RKD|HC zbNB8jSR%h8en{~XajR0gtyLm`RB&sA4fx2lNo6oQa2Wl$L* zDl6FUzyH^M+tW`!W1~imQEaQeJ`y)J*89C-^yN8>^B8j{O`2klKmLR^yO^q7gzxlo zjO3y5;@^9|e-B`UJHv(z8@)Q=TyZb$oE?u6-CWsd7>Wj{oa`(cU)J9pfy;&tvPFv) zyIM#QgH#ZTOMOC&uW&ATJTV0VAJDJb084sLBoJ&naSrF^j&7eNR5g*X08Z(6Dcc1W zBjjo8{7Q652-cA)`3>m6nDJp|Mvoq2Yu2o>4}9Ps?a@acvwr>hD7gP9zZW}Ikhs_m z2~`MS0<5yKQtanH|ApJGIM?xg=cXyn-CML-rp=%KtiSNk;mDp^BtNXcv?o&%?&UA& z@S5guzytHV@wl$O;3lev`3aAGYlQn&t6{otTs`dV4Jt>P~=gzCX09@94R;N zAm&*zof-w8A&J4PqN2jTiZd|M4&mr5YMt35I@+I zIx;-Q!QkZSokzqSrBErq{N=ACZQUo}`k+ROD5NfA?$f8pL94nt($P70=bd+YENx;C z1;+3A9`3skQji+40j*lK+9r-4V;dAJ{j!TDxnbKEnu9J~Bv`sxYx{sA)JGOg<7&X> z6%JGp&->HR(Az{E0m+ty)i|hfCr-g9qH!n%i=S!#lK!l}ZPF@+Xr0^Et=n38BS_l3 zn0y(?=x9NuInlekz_0O-09XuWixnGG^k{J*Ke{O;1Ym^-qoUD@ zh|!`g>Op!yiFJhlS75vVEL;oU!!s) zccj4xUtB8}6uz^;DFBn9>ln(hJ>}*0u)LfetmZboPlH1l+uU_f43PQ~=Y6c0xZx9os;_=7Yr_dqkOn??g96;sb>MYTj7-a#h_ykzRfX_h=#_fuExi2(Ud>$9-cj)=;Z+}}VV3sx{Wph%+2~%{}NTq9U zFGQNbnG{KA)0Rq`zi_2J_2P0{x^jISc`__dky`V66u7Mk5CmlE6~k(OZN2t9T;uy5 zh6l!t9&T4&Ji#uTIl+bv8Q@`=0hq4Z0HDQA)>FP%S5GguN1s|`voD^eO<)&z*x+Hq zhWWDrka-WF6)$~ z2_a#rmH<|YI@8{gl7TCv{#`Cd&gKILs#8%5r%#{m5>q@=LKzqnz!JK>LX`_0fM@s% z_j8Q@9q#iRF3=C^8|t}sZN!k%rp>S?pL*6_H)p2xF3N8;o(B>jP4&?K?mHqy6iL-)VENnC=d%#FIzm>?{FT7~V$@8!W(j$?xaB#ful)tXZ>NQjRwn zz!yl~sD+pls6XeDsdEa1{(wX7qD71RCJp*-aMzC)JAbNs&L#?AQF{y;$e_=P=`Ziu zGvB4IRjXDCl&*IV_IRe`0XiY7Vz6^{#+Wc_M`rE#o4hXM6zD=He){RBysAh}r0EmO zJ&rp76%zNe9A@wSD*NJh@3Z+YEtiyA;*u5;RHN*PRW+Kp5N)#+ZW{NHTuDHEb8^*A zQqPgFXcx`QvhJ_C_Ml0>uzacZ<-_{=YwGG7?Q`G!xw|U7{f$=|0EbH2F-ix&$&a=k zGAMc$cb8&$=Kt^i{?y+3*0=e#?D1{NcR-ZE48TgJ<5M62*7oh&?GJzWBR99G52i`- zYF0pk1)N9(`xWI#B*5|>BVKz#o#)e13*937)Kl~9o$q{?Z~RGz>zEK{=@d!E>`0lm z-=QgLwguYOOE){CDoP=NGQ9$rDCttF6Nh zLdRqT2Y7n-?kAw@;b4WjotvBE-^07ehd@8nU&;xAE}-*r;f7)i`LsEv!~en=U#LbhD}IA*~9xe0$7Cm z#Y7PDZKF!^8x(*p9i{J4qsp7*P@N_kuGzY^a&?SS-EP049G%PHu@au+gA*h(^9WOU zJ0&%xjT$xb*!uMweVcia)(H!N;}gU_-s=7MdtZy+<2x#|rKI7*%52H9m3HH`vu!}% zB4^q_8#=oF+q~zOI9OdIfT-NZ7GOs;wpi&^kjO0{pfI3UtQuQh}69S-8Q z?|3@V9Vw9j3xcF<*lWi(Yn6LiOkfSTH7b6J`OEscQF^<60cDp=8!HgXaTf#5g8}$e)yP%h{=YnHeHF>!fFvun{pzPx14FeJ9y6@80Spab z{&@qBS)0k=;4FKl8GT~lovH*NOkaX9T& z)XQyLS#C><+)yyAlX#Xy)&OX_?xBp>vikMzWv{>bV%xNNtNV9xA9gtgt)SXO&qj5@ zy&)-OL6KJ`z$!?l%a$#ZCR3sHxD$@m*4D^Wlv0_NR`+b|sf17mDNCXOmXELL=~+)| zIl#6HarZOo|Awq)rtfmFS0 zRn4|%&mO6O@>&O#EXyjkRm#Uokz(bTN|+n&A(2l6YEdX>{K2M8!Q=aMv)!_lXAi+fRXU$&Z_}Nu^ zkS}eBbc78nFF)6}n+Iri$)ma@7Ui|9w6A^Y%l~8l_N`yo3yW4s-Hdji4xp8#8B3Q0 z9o)?Qw=S7E(blZnWQF;8UiQZxd-N52LAe;<=#Z2@nc7nzv^&06-~H})HQQR!Qd3i% z&iSUMl>1_LpLld?pY2)w)!tHhR$q0}nFFFN1qFEr6k6sksf8J3Wo1o@z>qp;&Kw&e zI|`kQ6bK%^qZV)t&v>rEu<`f!L$8sKP;qUAE}F}T7rZ0liljq#KX`VKw!lH?+fpYCvj*82Ac8_ zBV7_+v}B3eDBo>US@QK_=}w6jH$V5%GJ8-4czm*0>|89Bv{4SIyrW)`WSg6!cTi5o z{pU1;iTlNXfdlQgzx|yN%M8^u-u)%KAE|-8E`wR&waOQvZ5T*`1bW|n_t_(lJYo|j zjBivNx=hh~(e~{-{;%#$7*#NoI}riPMbMg>Lpuu#^QWs*lq=j!!_J+R8F`utpE+}; z8++p=gHRb70B1pt!Oh{d%9sH>e)hAUxnX+pqzSfQ*&6MNU2N}t`|I2m)X;F$4cSNK z|AeZ=Ox+KE^%wi@&mM@DFUcT}y$R-q-Zh%o0C=D+H0B)@BpmlgR$M78kJ)p5f zwi297a|C|ar`E0CBx!4(jOcsqirLd-FUzyXo>^$mFIjJs$CTN?e!T@i+K@z28aqIv zo3M)xGlskOSKIPcn;h(R?b_o$U>9FJ>lK3(Wd&RTvgBYE^_I{sn>TN=8*aE!?cIab zAgF`s+H&tpl1N|Boe3pfQSw9tEXw6lR(^iZI-&jBCDNy92}a994?Uc6_0?A!em7l7 zSv-s*DIFw?{GlUp?X{4g4N}R96)Wt<8*j96+T!d`%|Y9*kYn%v+Z$}Qq^sbhiQ0w4 zvRih-FUarep(hvE_%S0Sxn$bvwVQ0;{sUS`pm^_+qUtp>h-$@*U6w-ha4rlIyp!*| zH_+P+=IN0NPmHC7@}jQOfYPV9NcO54+b(JEf4=<%d&`a2+WO7gw1j7c;+kpQVM%{m zwRWTZ{=sML(i!8eWI%5Rs|Klx^da7lstRz;msFOSk!C}*eCw}&eNdk7v$X%@a0f@q z6B6YDTjoY?u$&v)BV120{Ry#B8?)@;4 zXp_)@rR6ylO-)CyROcG1k+HtAa+ju&TAV2XBu4&@j7LLoJ}iEHt*HRioz6-9i3>)2 zuz^}DxN1#>96(3fyWeuH74*z;DTgI5*)nqDwGLR}(LQN>xq#`It(AS~hPiWW?vf&J@US`8UaLCFKMaW~bWEP%J9Oa~Byi5Z){>gt>9=j$ zHoNMotF&sM7ZS2ORT?vN?K@Hj-Q7JA>5`~`1+CKLJ9Jc1))9fuTdJ$8(#MV++x*BQ zkEUKM=R-+ct<2kzurMT@0_J2ir2_{MxZ}MS((h15I`fZy^h5jl*T3OmqnWIbG1Wfy z-nZKLQDtrqqTYmo!AAAp_x{!X^7sobSsjuMYolBmZoB0TcJ<}6tYlz+n=r0ips>ca zZQbF&6#&b@03W9+?e_?PA=P5sX5wS-zI{FmxOMwBt57&&u4icpPw!p@wqwtJ+qt9C zzWt@U?aIq8(sPOiE3m^wV#1hmn?7Z{{qolj*rA3d8#7{%Jo5M3>P%_# zMv-DsW3hc9X+1uFv1Q050?B*Ts@1N}VmcrNn)nZWz#Ehs)s{i;wU#33D$?|R`O9BO zD*da?m@%WVqGEHlB<5<32mc_SMxy`9wION4Iu-r^Oq(T{t`%rqCc|)LFF|v1eblZ~o8kZRGG$SG54EzkA1MfOU`lPXXBE?>Od8xICwk)kl zPfJH=a`m#l;fM_ON8GoH=&t->cghwhu!M)@K5N!(w1)bl_Ve$5*=Em};?f%B!03;f z$Ge7?4zY`;Pqh1f_a{qD&y?}Kzb}@~lZ|cKgmP<;)Wj?z>L%(et{E8K=Py|5_OHTT zg{~SBn}77^(XUVqc^`ldXra=+7D0>pBYD66^*7*mW&8K@=IjHq+HiamSg+NPcR<+0x1-g5nA9`OKBg9ZfH+pKt2ci;0nZTy$7 zj+`Nbiw~Z?=jZ{H(RF=D7bNcpZF-MS5S?X}m)gZo&#R@x`Z+7?ojIwzkKvMY<4}Gk_IqDL?Uv zkK6aZ|6ev`%4BOiQtPVJN8fj=OIO?%(Sb$8*|v7$Hv5|oe%1070bywWBDn{wv(J9w z12$+tKaa`~-?`&y3C&Vv!$OV2Xpa#dRgkd9v<;CTklt`lDArzw%m@~;I9jv^sPu?G z8=q&#@8pA|ws+qFyXNLQw2Y`!>kjK}SLH7I$=5$-V@3{DK*3$^lEJf-iEufOKD)@i z_p?8G6o`zhT%RR<=R5z_KK}7fxDg&Z8{pJ2!5e@Y&aI>ifiF7N5t`Db#PbKx)u%+` zY-!kyS*q_+MSs0a175B69?Vn(;8%u4>6l?R{eETZbyGn)c`ab&`kSHw+#n~?D@61r zF1w;}rmGwvxSvFa<@=M*FO}ovGMg}})Rrt?V>iEHu4WO( zxOCGMSV1osA16~(uVUGYh71~@2o6JSZ252-H)@1bv*A`gywrxOL-f|{6YB%eT6%`g}Sx>S3{!fo6|5DpgxyKz!Cyp705a{Y?RG)}ZhA0&CQAv*T7c5gK znx0x%t$=@zJnHHyHZ5ibI~&+27ZV16I#PJx70P)s!og#YoLEw5BjyR9jQ!>&X7? z-~P>>dg>`>MjE866o3h4#3u;#DV*cT-}#a#D|H2|WPh+PeBtxu!k(KV8YGPLyOpw{d?_wp*QF0+KBN#jPxh6n%oiVti|bZCs{ zBc-lQ9yiK?s*e^UW4xzh1E4r};%jwSAWC!wJk_~|k^EE=?`vvqvWuop^vA+7oyBq# z#SMdP$&jiz?{yPBSc7=;b(hMZKf$HTmZL{}V_c$C{P@TJ(;DcJa&}DE;IL<5?~H|3 z@p7L}e9^H^{EjdGuYdh(`^GoE!5*9~J9cbKWzZHM{b<{^owzY5Qp$#I4}siS9pcZO zFA~<@iRs5l1v!?fsVO1}$gV|eSf-}Kf1y1EU-!D#`LuTMbdRrO$M0c8#jZi4(ede2 zG%m+PP63n|8QHh-U;gD^#DxFpzG;nhRknEfS{bd!+9%%kR-ZuwFsQF5VH2Ht;dBxDu-AGz=epJJd02=?(J9+Qv(v#We&^DqR37(sr|o%1eEFa$V1={=fH8~6_49!*0{uH4X&>tK;)^fZ_19moL4FjI zS52B7P1ChKGPK`OQ*-!WX=!P;Tv8Gquug5*>qw2W`#T^9)2+q6xq$+MM%$;}|27-Yuh{KN+)JWPK@!=qd!L`ffwWPQ(CCrF z9LPGWH#EUdihRnY`eB=b?ik@)fhpGU<2%2<;8lK59+n1;6|j4oy&hsSh*9bLPzAlL?NxN+LL>Scu)`Jy{4VpNCej+f$@6BUsu z?%?{syP^g?UJmE(8fBqAFdu2&yeGAv;xsL(__JnuW?OyDK6_bUb@B8G_J2R{b{jmf zul%4I9b`f&m4zF=u_aaRIpC+|F5*YjIuiGEOpF@G^b9kCfR(Fw0x!VoTqN3}P#*0h z)KL|M-HY) zAMv~btvLrkOA_$`4XK^?B^qCNg}eBGml>1^wAejAz97N{8?9%!!nRw1uZO3FahqH*f`e3t&YVzw@2%*p*jarOs5V^GRP0F;1L!4;U{Uq0CnHp88I`0L=Wf@;aGE5QYVXK85iI$J4p0?I?{KYs=ELu@YqU5R;6P#ub?B;mDUwbwypvd3vs)o$0IseQ z5)KZV+jj2O&N(%{Bqvj&Gy{gyR1c<5R(>n*VArDT80m?j#d-db053Wf;OCu&&QBg4 zXv%dYza5pk?I*wby&P4?*y3gD;j2U$rNne8T?epZ?iH7tz)jb<8DPcQQ6#G${_sbx2IG@Qu)|w!xWXq-9(ed=ZA8*f%bbqM*C#d7x4W_2 zr}FBA1+1PuUv@rhYDz7TtdSd215#kh&{>EM&`SXJk@wuJr5;CpJsAwm`l#Rk{`WGR z?zAs{@k=&xzcvHBk$os)UKvpkP?F6w;I9puj+??z?$ z=tuv_m%$8_LuavKSv5A)+Y(Lf4<1rtpZV9jY{KX=ms*0KQ`hf_Y(Ch5`f5)4 zc1#PCtK}wuljrdgG(V(GBdWD@hJ=GC(i91xtX1syDgjzmb*=6@?2e=W&HzPtV55tk zT6^e@q*|9)tiN2Tae#nJV4X-_JWKoj^j;6|AG4V?+3YE!wYIUq2P}Y+6owDzh@?gP zRcZ4Kc!odJLk72-ue;0!DRRZve{jF0^r*2>L;7jGVY%BsZ@cX_yX($7?d|V)hli&j zUuFzLqD=x{pqkS@@I;|D(?I>|&J_3udNNt?k&k@X?!W)nHf{PetCwW8QiI#HNn`AN ze|w|-^@$f%)Ov-T767{~uqB_e3+N*B@6L*x#Jdv~uoTtTlU%QQ%1oj`+SxLtpl4o; zaUw|vqO)J!C;!je?5kqVr(aki=fz<*al&{x$h{zY&y|Ws`CC?``$&k|7t)EqCom|^ zrZ6a@Z@xBU0Kgc@PB#Sx3(v=wgLggi%rkOT__{s*_!9!j44W{1tU6DFE#I(RyjJ5X z;LX=xs<14*94yG^L`HDP2RPunG_<6jzdFpOzPxmW&JA!$s&i=z5XB`T!?I%_QKu*i z+js6&G=?3vZexWlRs5=>F*Q1>7xd|T;$wn*xMt6o?9Qe`^j-iANh|;$<=m+yML+-b z|Fir4@K@QOM%l(4dpz4qW{h(Qn;mzM{5mFRn-3U@7uZXY-+cG@U#bN0wFdSrvj6?V z<1)stQ&`|ZHmZCC;*|aHM?dnvfBo0bXcfg=cax!QkO<>dMSy`6MZu_Xi3{*L*9)lw z9|itl?d03uc8jfCxmvrWPqV`{2W<7a&5}+>Ycl2?4)ifpiW)S6dLOBhfYp(cPVG17 z6lm)N)kukPAthgt5m66=;gH5+R;XX^Li>b33*+mbAAL?!^+VOs$7l`OUWEw0MUvJR ze04gaJfd@}O5(4Hq?)dZ7#I{O4RsN)LfQhj0I`!9VFPpVj=*5???W1V_SyNGdjF=) zpZ}a+nm%>1Jo9U8)An61`pPHzrnJP zDdK{*XCncITA1MKC(@XMUA7{Xsrh1YX&}dhT+elsT1L`71 z{J=AQ<4JyyS3GZY{2j6h=i~2v_~D0rZ7ugqn>IyjJ9pcL&3kOT)_s0T{i44>nz|E? zhZF|5l$1!qQvE`c`}IWqIDIkU?AIt}{Y>elK1M_GJB@W}f){;lqc6lKq5!JGhi-qf zwx=qvAK(9=2ZS4-r5%0y_LTwhZmEgCvk!jo!&+-M#U&-G(2)r_7Z@QNIY$ai2*bdj z`~hAtbNHiUav#jh^YMRCE^VKT(CgQ)_Z>^_zWZ*!z~Ft!?2D|rN`6&ZUekO;>RR7^ z_7DH(?RMp?Nm}kw=u*|OCVaA>!^tPgf&(ad5;ARxZ8a=Y!2L7G%1FL+d`9g2B% zyfjv^uaJBgcu+I{_?O4p-Hud6s@zv)dv@)%8?TuqTV3Sq0`MLWuy_vRJ9fQ3Qh%!z zL+fxIj-pXV#jeH7;*HuaZ1nIU_Rv!=*%J!0BR$bd1zgjnPVzc`_OqW;PM_04;Sbss zSIlu$5eb_%hHnDz1mFrh0WX9BjqfKbjd$d?j=!lFDgONC&70-L|6?u9`n-)8G2H95 zZ_h5ZSEXHj*>t=8t#jq((#O?5`VOjblt(mDD;%=V5^CV3L$&W%erIBRoEpcUyRtKU zI$%j;zgs;0`2oH28y7BFo$;kl{(Z_-n(?6{g^?x{3r5BuPpt9>=Pj@=fA?NpN|lOO zYWW4l_WX;{&bF_6-CTRiTi#-0$By$&3y_jv0`d!t$dTWqaP%LskH9qH8ICY6U{4A91!op-+1!yRMa0>JQfqi!76 zty?Fsua@K{U>64pThx0}H2P=hx1xedg4(uqm);qv?{~cY^>*#0Q)PeZeM%q=y`VjR zhqO$nw)TimFa&_1QiOu$+VVAKOBWuyxyx z_vs$BqJvmWQ1HhR?jw;UEGL$jt-xVFYPA&htz)E&QtKE0$*2ohWyqlTMs3561LaSD z^QhF9RhE^vSB{mFEnR!zJ^b(^{>MYO39vAh-FPr+Bh~{nFcys7F$Lz~e7Me$Kdzx_ z!mzkS@W-gjv^l#IuUN6%{`Y@>-Y&~4=UirKdUlH#@QHcP_%&iX&AoK8%}^+n(!u@Q zW#EW9zFu%b$)a86o&3O;3S;&F&6vISw%5B&>zNmqS`Vp+Q#BJuC&F;Pdi_Rq+Z1;e z9W!dUa>95m@X_*@wd>c2Mk!LY-=cW&<3zim9syvFw(epH3@Q=9^9D;*q;rwj&4y?Q z>=Zq3-cxqdO*grnW&3umPS809n~GfrTz%_Tn4>j>Emp^)>%HkaJffb9V%7#l36q5+F_baoacYk z=ep~!lO%kJm6vOv5qR~|Lhv3va-44fCKB()cE&)3DS-ri`2(~9a0UMH8)6?k%O4hCRM_S%J1nobw|)5UZ?VfX$RS;lN5rS%PyNfx zjJmv3LBmj93sGceup685cVgN0P@@HkRF zEnCIce*TEPuyBc`^~keJE}m|gVyX=rH`+b-+|!yJlGm6qW7N@x__TF!X`qv3Dkxn% zV+Do+oM1+nk{Lw+3RREUnk`}?{HHc=u4sM7@Up>HT3Y5Hv|sl<83QS9xc;h(ZPwH= zHdgjG)HV#{NK|w%ck2+az1l<;P!e*M4(@O7kq;LU8-DZf3$F5c{#p4_tD`OHH*K$$ z`kZM6IhpE^xss07+xy@DemSjP+d28q?e0Z!L3*5XY0X0707IVd^%kAV6E1s*t6u!^S?bq>!1 zeuPzqzK0YDFq$n*5zQmJAg(ggCyuhwBZde7CfeFf+qD7hdhPnN)Uzz;-PdN$m?}{1 zp*)V*h7D`&-h1zhf1QF)_Lr17*6WV+Rj4(ufL-Kh8`-reQvfa|D;V?`?AW+t=gu9r zYV|7HAW1hqT|9G|06kOwdHd|KC!X?4m(QAFmr61mJ$$h9J4@K;1MLz6l}^O>pV;-t z3LpJ=^&?`tm!A0_i7H`*nj*ONWQ1*$Xib9APA0(&{9%k|wrGS_@DCZ(*Dh1&ku?fl z_u`T@_WvGUoym*kp1#B{>a}cTD5=Zke2MoUQ?lPz+I)10mYI+3Vk$19bz<9 zx$TG;aPNU?TOi4FYGb<|?ty%=fAnOF5}QfH^mXBw#5$`AoQOikxS z(qO`(pX^i9Cu&wsbq6rHj*}@8>f+^pwh^Tf@Bl`D6@VRp3V$SkY0{)AzRNUf@Ksk` z)e2UCMd(BTSCpdu!IH-CPC6AGu17Y1n#_nv)B$Yb>u{>y;Rh1Z7G*(A#Kcf1wXjaW zrKMjEY7LFiLUE+LQJR%xR&|ynz{)-QZGFWKBgD;umlnlduN{80OLqCF0+-+b9;EDz z>(|>8JF&x^exkJF$BuG;F94g#i5C{E)Y*E^^o_5()TWFb?n{u^be8(SM-GI#;}z&5 zrVOql;wUA0GL_o1f&2sloO!hFO(UzkKaUeC1DRND=YKw0j#)}gt|itzj0t?#t_>h zTX(sgk{i^YkhH=(ICUmcsJn>mWu~L&2)L*}QWtFjs9{%&297lMmhaswUjT;GHCda^ zR@?TC&}ZY~9**ZAHs1w&7*v?hl4~OCHEK)PsC+_lOh*wYUC3SKK|k-E}Wi zVo<1-y=}$jy&Cy~1fOcxTsBoZAC@@(b3SQUN=b~DL`mx-I z6scmUDNJyr3aola&B}71ppB8d$Ra%F51pZpkSD;wI{@3Akl`%avnGGbN5~4DO^>%(T_Q8Y%iJhbc)&9=Nu9O*pm1q^w z)^9tjAEBLSBbto&VwlGCKtMpGEoKrKT%ZLK688WQ903n)=%)cHUn>3p=Na1BK~JF1 z^K6)bvnzZ=UO|F`7jVi2M39hRcfcFz7IhfOGh4qstd8Clg#!ve72VqkR1sTt306)- z-Q%kRvU{p5)Wgqasy!pa@L8qwnaMn%0ZUcvAsXiZG>2>JZTP@mk=U$;rhQI#IDpYY zQdU0NM89Fe*f)J))TI;Vq(sxyyfHk<`=jwJI>3CKb6}FlPl^X8KcPJ1-|goT=p(+o zjM_!Lm=UXM9VAdANX-6ueN|^>!1D5<2qn;}x4hL!d$hs;BXUQ;0_@hTiTuoPNF}@t zk|~|jJEUIG9W)BcH$?YwvtNjxc#s;2>JF#@Vp3hnn|DQRK&~g3;85}@>D|+9!r5|h zIM&jVsdjS)S9?W;Il1h*<*tMVEH#SuOKZ+y6tQD2`&b@LRKZq@cnvgN5`-3ZU*k^wCoQMA8Na zx<9gs@f&(V55Vp>zxl0=8#B^YZr0Xf!~1#^1%Q$D(Hl?t}rk@tBBOa`{Qkhfj>`$PP~V~ zKR-9yb}613lN(BaRs2lhsbqQ1?@d&|axMX|7H{hc zfQ17s38TEcQ*H8?iyW}%@QIx0?2!!3pww1F zXBW|!vDl67F>?aIgcO977TEy-`v~xZ+S-=ODSUJOMFmZj{WjYU?Ub8JzLz3X?{^7H zt~!3=6mmW1wbvl z?uCW&JyOUL`D5YSh^>mHC!`<+Fe{6%8i~$~x=6zLk%Uc$#Ox_HBRu&-xEQ~X;2$n| zyTKmNkwhfbSk;IU0K>l+&;ZsQ0V_yd3=D05vB%?{bE%Dcbm`Tg3C~gnq-G|H8a0W< zdvz*91?j1s1Qpd901VGZs%1n?oQ%s)TPVU%L%tki?^3+ zS<|;|?b@}zzAaQ3hf-1>t#6AYi)IJ5MT-_`q3%chT92%3+b`FKH(fW=H%4XF3~)T3 zQ^co1T54Yt<|?3LyFW28=R>0QZFz*VXZjd9#9|Y}#1bPp0>ODBVI`tU7|*$G3=~0` zc1vNZIZ!$NrZe5F$^6_rL#rm%f%RUE&)daADe%iEfbR`x~yl$c@;D@kpSgHX;SUP?s3f=W&}Nv}CA&9t;H)J(*)-bO;g(tot9q=e-ZdG#MXVgr){7@;vBxEnlQecs6& zPLcu)5*W^LDt*)3OFWXo7w-9`ZB>xC;(q;&9enb&u$OV>kQRg2)Eu;l<40@3ueQ8X zdmWMlwPMo_J91#Rzwu-5yVb6nGb8e4(%=&e^(R@J& ze=c8Z;8#S(z|pV}PwwRRb4`JuN&r%75N{IfT$!nz zvl9U2p6ojEYs^M*@6f+cuCn4 ziTPS9Qatd`L>gH50K-~S`07U&ms>9-zjOx$)CMuYYHcR1*L4X?s}(})aZJdR(14{* zkcADaxhbNb>)tSZ48RhSqOajXMWFN=Cct@8b%F)PQVgIX8&=qDJzPx0W1@Px2U@ue zD=bxTJ4h?`hp{`{nmi^ckfgxLP#|uyL%j(Vy~U>u=gm8rr@Qx_1P82?Y}Aghl0V+T zFsWU1FXJTNex6c5+6j{&@k!C$BR_{op^v|maNpaPJi&@5V{K*xSdm!8GLd9TQXolz zS4V-Eghki#jwJ~zp-n^awM}#ckc1V*8=}yJmT9#V#@8bGJxPHi1x}6v>QM2T3J*!b z3V;QGCA6(26!hdOdv5Q-RmW9AB07?SD4Xz9J-2i^CvS^nT)oP_wEjBHcdJjtxbZ|awSN`OW7zLt<(+Eh~ zV@hY|PCzzMI|7o($Ex$Yqno1pA{-0fJHJ0*9p%wqR4hOES5+W(Dtf%#k64be7eohr zM*mwcB8?~0i7C)#bdLxo2`fw+LiL2EV!{HJ3{@Va001)>NklkY55FKKCk$bqme0k9ID5(x`f3cHI+D8RC4hmS&qN7fp) z^Eo+t#1JU@PHp%W^(NSePU}HcwN)72;|@M_XufkhAA!&G6{lKXD&x`Mh+4_EWQqY! zGO^7s&Jq}c#_4*rSpwTsY3nk1r_-@n%h#ZdLs@{#{dlcM%W9(h{248FMjdB|nD7qP z0fv0ZhZK&nA0FzMeFj4cKu*CR(H0Mr(Chha@3fBX*T zh>Pa;DG!cew`0FY&xZGf_d|mS%p!0L-`m{7##9fP@_NS@qN zbGX4O57t@JVWNRXyDoPu-_)}kEvZY6ww#~}0UgCqo zTVfDN!?@&+&jIMoPxM+CY6 z8PZxSpmpT6SbO;c=kZ7DI!;y8`L9hSO!!(QEMPS>a;?$6dPp5v8L8GYTT)C;fy!3s)qrUu z365$3o*`>}iT;NZ?XR_-?feU!JwBd$=XbSIF!m*)XN;MpG_1cCAfl7|QTN0+IjCd+5z-8xQ>SgtYbDLp z2#{)OwT1b?T02-R@Y!2s`>JYf**XQXJ6hfLqE~!FvXE5JvxntLlIY(j*D}(2x~jx$ zNzf%qNCZ)*;xpElB1nz;RqVd_L#)Hao$@o`Lr1>)&(?d||LSltx=V#abxciD2%4Cr z()xIZFQN3U)$r@BXQN-r&wG-%psN(^Gp*~<{}{M(e~d072q1^byM0f!t*F?i5YO5# z@rVWi2RC;1;r;^aS5oMk^$zG$Xnl(d{I5v6fwMJz&)giB+K}py&Z5mwaAe{=@qaP8 zg@#bh-demOK~jvbTT2wkoFK(JZZQ%Tu*CQnBhtNt3J9_kZ4|KR&~1)exy~*|eAFQq z<=X~E(RY8Z7#W5{O+ZzPq@xB`w*Vs~p#uV-z5A;LMh9){&V9CQ{qevk$8rjKSV`YJ zsV4<)lXDPOaRTGhxq@2KW_yVwi>BC)(w)^SA)*T3iY28LD#lmOJZ&wXmF1B5qu zMsEkdX6?gwH0qEPGB(!54|%QMJAG5~jYSL)$?anJU)nbQW@h#~!~WPpH7n|1UlDsUhpSRwW_CI*~$l}{v* zGbpvi4dcD1%G;+?|L~04?)c#y1d_D6HH2Fd_L#7MB?B_i`rNHS2Aq@(*<)G>-32)# z(VTWr+$G%u6EGtZ{fYT>4xR;=Fi2xdBHnnlKxv<%X71j1(6(3Z)1G;|?d4@_b$76Z zgcqh+?>@b4?YS*K5S9s%F+rs@#EUtCT9YZRZ|a zw|S?%v}Co)1FN;AzS{MrD8IMc$aq;~tC9o;)78ioKtL6qaOMG=VoVfSE86l(e!H+K zKtE?-a#V^{l|c)h3o1TP06*qSlA1PokU(s(^G}_m`Hedd+DnU&+#B?lY9mIDuu0`o zCx;HO!2=|P_3mXwnzal7#$XIT9+TwGcYi-_F|l`_?jdw42VG?nx0aABUv2iMTdb3M zE@34sscmVZWb1S*Y8?ayq_Qz^3a9{+BMl8!z5j3oPP_NZ@V(bIXy3c17H_uZx*(!M zi&X31zrQv@=;8i2YJX>jhQ?!dNV9s3h%klUDb8@_&4H?)+_OK#%yXYYwGM@3l z)TOToyxJT}v~|aB z+qk9DR&U&HilZfv|UBjt%V1evT;)aC_w3`n8~;PmZz-+HNzIdI~u5oA;e;Y6UtvlOEImBw(E^@w&P%Bcmnt*s+vUL~XN7_i1|a zxh1aRJuk6(*{TgXAB^HDR@A$%RJ%6m3GZ-bcIv7cW40z{2-P@AaO#TW$@6EC0>Fub z3SX3PcQ{gi)aqJ}>U;!X1B&x(u%s|1aSk78w8fkD%9szIx7grPZCE>Ii1v0AfJp)y z)UUU;PRnsK3CpVhFp{sN@Nb=ephHB1vtwuE>5h1Jo}`2=sO(YF#nMu0B@vZqre$5l z9((r1pT)?UZiPj~k$NZ9kOV;0yL#8yI*LW390u?hm&Be<9+DJD1PVm_8I^?DN$gwz z3KD;&Y_?-dw7f}w47^q92Y+~UrTyW-m*nGGU~{JowJBrE1YkpKu+&E+FVqk21q@+% z2gpt;8YC>FvsfbHC>^P3+B8df`Q>mr@s0@$Sm|ln&BM`%24Layph1JQ%!I|UY@MzJ zyns~9p9x5!fn0@5;#1;rTCzkZq<{lh?5;)`+L@6Cd60;Tr9Jf4Y$U4X5!t`yE!$#` z%v&y)>0xs(8(|kuDYxP$?a^%M~qF;u!^vicpdU4fuf!AWwG77u(+6gv$>S!w;R^m&F$Oro^d3aKrgo&iq zb5aTG>+8>p*`O@o!eyUmU?02A&cfad5 zp_s`w3zRsmV;~=c^|9Etcbr4>7*LdBCB4hs_I3Z0EA9RV9<%Y|#@Y>YCfl^}BYY_l zj;cspG!q&M373qhFSVpMG&G(UhWCU8tn~Dj6xkA!$mFbb20k2UVyJS;+37iXN2jD| z@c~PuQpMZ!7X7sj9>cgSy@r24USDYB zWR7S%c2I$3mKJI8eS`hSckdN-^6mCpFSW~Nju_zjeTiJxW{iye#9t%jjK_T6e~WaP8#P@*H7+$+J18XU+hiS&#^aNGt=XQS$eLU zSGdo#FQJygxr8OvFroWg!U9%vGaj3-azswDv4BRl4@pM#n>HFtc26cTWds}mNb!mN z%nQWMJw}eQtimEo&&#v)Z21MH%hxR?{UA-HNF7W)s^Ei%YV2s$L2FpQQa*4|e&mau zPw(K!Z(13k3q6H%;k&;2HOiD%Rw@Zk-;mU#3MEtL6ky^B=?R}h)W4~dhWhc34=%Mo zJ~+>=x#D6i5gMRCkB1}mP!g7}-R(TPggTqBfEBTKJliG|-Oi7~h-owOH8rl3Gvi_# zd(ou|;+G>Sg$^&;f(#)!sWtoBP;W;M)r#SpEiE(K(j}RtN`**MM9=JesT+D8fTB~R zXnkiil$K@9^?Fw2bbTh2KY4rw1%j;#;9@+PHn~Lil-RQ?w%OpKT)9Hz zDP)pgOY702$9XXVBrss%(-vd?S2RWP#0e=Nph(Thv$}O_T(Y|9Yu~f}V<%YkmQAi2 zIyj(~#YskhNv6OfxBo!hlOxp{_O5R@>oSl@4A2W7OttNDAPk${v<;*;QWFr+@Ta z8$077YZmBv9b)D80wq5>r9c3#szdeeA~9-Up|5=;?tYr2Z=|c{=3}lBcA7x~olIE3 zYH3kC85aTCm^Xpwme@rRSfof2Y23O&rix6PdCR-4@0jtnd*KV#BFQRmK#5B@NG_<1 zEwXz6P)J|tJ$hKZ)VL%2_gaIFTt}kfTAK1q7YJtLV2e^@4B5m0Cz1n8>^oALloCI8 zOlv)xBoT2A;H#6Q1TbPx>(RT9Wl3twV5F~8$m1h*4vKEvSN^CqfY?ESE`T}W`kQR; zi_dC}sK8TFTdLI27R}5A&`s<{>Hg9M5BI}zP>rdLntk_~!VCcyTDLYPX-H@(Az=Y4 zYThJRoidxpz^Y+Oh4sGTYMXJ_2Q_P#ZhMz3^l9vp*;hJ{Ae{v11>k9vBvre8i|t># zO6pdFW%VkyJV`_uJ@cgo<~lH?WoQwu01t3U&(4-~R3ng)9Ynm&wWG3GHOtdFr*A*E zGXZv41%(cDE|sY{kJZ;nYCCKV)m2uvyV9yFHn~*Svt)=B4jbVB<-mn4On}OaWZu9* zw&k%0ofMb;`+wTO%^PgV*FR@XM-+BQ5+UVHg4W4%iaS^P?GZK!w9@2gn&4Ov)$hD0 z=gfOIIhXO#F1Zeg9_gQ`QL2H!c1pqb44uwR9B$g>jDHSzR=LTGKiK}?W zBw6*6fqv)n&sctG^zQ6l^28PxB3%Jc*tY8S?y-X#*4n-mORYfX3RMP$Zj7YBlw&T@ z4ZY^|wrk<@lGf@ifACPtDk-%?&(F7vDO2T5oU~`X8qQZcYfOu+aeFlxor7o37r;tr zG4&j@(@DP~HfFY3{~9Ir$f9b!$4+#$s#&U3nxrGtvOSWzYGu>vIi%DE&Yt7a5Oyj6 zs8MRy!8I$bc-%zWvuJ^l{+MT75Tl*QsrcNx10fkr1CYmBvrSk(rr!v+M%7> z9TYK^lMYwvx74^yfoQ4#v!HB*tD1#n!ySZ<9N2FM*RHk$t5;Z`iBtU?27A;>RL4G( zrrCah8UX4p8f1zPp5zWFaT*EY@n@+?3F|b-mwe>RQNa5!>Y-GsG=W63Y(32aBh)`s zv0X1cXSuRv4Y}re2ZJLA4_MWvbyDS8tZMx#J0`WSiTL(9pC@&VC;+*A2Ux#p7fVW$ z)FZztq&X7cgGz>q+0rz@e`gBJ(zA115-b0kJ3JqxD>tyqV9xvM5A3r-NrBkLF!tvt z;zpjp@35r3J&PCG!S!ogT1(Y_f&eXQY- z;Q1n9og;EwIIoGIBqnc3n!-&1u&Uj$&GtyODw#9aX9Mf@>=H1nwWG2{`D~!YW;sBn$b}A1_cX;X%Sg|2yHpQ>6{&F8 zNPpL%ojV+W>UQsPH;4Ltd#(4xDK7n?k`8i}AveN6`ASp3Z?nh#;Rm*Je;sx^6D)iqN z+WSnHB1uV`p~~2f>%dW|Up3pdNKHK8_AcyFNMVsdoaH*qhDUh=SdJ!uir$6V3BYhq zMvq*}R9FGO_cAu>yGTLKL5Nh!B~9U+&I>F!FA8}^+vc^LnZz!nAIuh28`nwoTVuKZzr8aLuIsMu_>tbzE?JAM zJ(8u^j^o&gvp9AZvOq!_Xi9-FrBFy&8>T?vkA`+S4P~a&4jl?Jw6xP{={giBO9>&Y z3B)F;2@r=QjuR(#;!T#VSdwKe*7hXz`#JYrS#fEwq^IP*`1ebs=cjk?@BZGo_kQp9 zoZt7HpMuvF%`H~h)atP(Xc9Ac#J9$e_xh|~k+f4K)wNdE(crFIp@RB_iqX3KKrW}N z+#Rjdu~R$!l6y!T3pbQNIWvkA>KBPZMiK`A3cXiVYnv@=@35+KR_eINHLg82+;z}_ zs=U6*$4i_nYVqsYd=hj0-Tj2gtB+j560QD{>m%f{zW`Xdv}WS^(zU019EJnCa<(IY zAwUj;YP|ePNf4TR)F{spmG%m#kR9;Z@(VU91*g`5%t1+=p;8Cbh+EMou_atX+(r3= z{&%Dg%vAEjwrMRB&#WNKjQf7svpPCr6DoF!y z*@#;W?0rUQFAv)B?|#8X2BvMppMTmKFS*=V5kNFGrhG)-Z`_#?!T6y=`#m_0)B#aw zCaZ)e-KI>;s6xT>Y5@=NC>Yk1K&Mdm1dNM`0Hg^OTCu2i$4_n7C;rIluD;f4FWX`b zmu|7@l^qUTfE0j;(w$jBfYDu2kSnyDSU}GlwHZBs@?)v509ZLW?aAxJZ)svp0-CCo zogM@~Ax6`;^HHmlCQJ0lo`3uiJG$*&JAT*KEY(v=IRTkK4sc)wPua}Y zBj$yqp?cMMHn{&;X|c54p4iXO?wvOHz&0z=W-}sRO}BrmVf>)bd1 zT5ai-caM9sJf;t^EzZ zWy{ZBFTj$EDE4$<(>-?N>;G=6KKLgNEa~Js7%*hD{i{hs&2kT@3s8ZefL=3_N{i#o8RuUk)_M)U3^-me8hU`!iFTv znCw4hg>p3+YiNtMK!KJ}@YI||_q4CTT%%xo zzk}F{4HvsGwEmLKHXyY;IflEx@Of*$=@x0W8l53Ez5hct`0UeGyMCkc|DvSnY}Y=> zLb-Z{wT+fhpW}OXY8>VIQ9k2ikJ>K%G{5<+w&u;h?ToKy$ItB86OTKC^E|=<%~*E4 zEEawR9W*OPDJaYfEXa#NzJjifm*}e~rA9|0p_wFR7*Ersmn&X1tYCY!ZTjNf4gjOl zV2wzPJ}GGd5{8N6eKxS;;cQBbT&^sG0_bopANl>?wemae^l~sol%0Vksc+&}gZF*k zCSQAtHLI#Zsa7-syoTjMGBeo!t#4S(x(jXXJKp19NGwXBbPuXeXnd1zmI9l!_sCMA9-~Q_~0N&Q|c_mHG#1fqZe zvwF-jqQ@$2s-3v-J8Px;$_Ugdq^4hfq0({MS9uY&P|1D5JsZ_oYXXY83TlQ2~K6*aaf*0kJ)5A2I_02NQ85jcmnwOx0kHE+JkQ*}n4@6qhzJ|kvDz`dlV zCX!6BE&CyhP@f)dkM1<@EBsoR1@zFE<)tyhDlIKd^nI6Lmyn_9DIeP6ETGFTy1}EE z78rH z*W*^F**hvV9RBQIsXFNODtv{QQ~)Iw1t?Lo3Jn&=qq$3{_!qN+{d=Bv7qzsuR!aIx zTcLPYosy!5lyBIhyhq|%O&Tw`km^7{8ZHetVorYXSmHG0NBkwR3`;i&k72AC-#6Gb zw0T+pw~H#DhqrI@dREO>T;uMar_E$R1@I_R5k53dUD9RYLe>f}P{h0O1v1pVGRRS! zC&;VxT18E5*8k8p>;2(9zA{ePl~)OzMukgr;*b1W@u)~YMITXx2i&`(KaT*ZR1ye8 zs>G_O&cOcF?HwM|8hCE69e(hB1^buj#)2;jv;?f7<<0`JOPISJm!_x00@l)e-LtLN z$mKezV0it-o2>crt87ZZfz*M&^#tY{ zF4l7Ca!@Aaasp1ZtIsoPW*z>?0~+rMt7vJ{tfq<(#(jm?MOnblkEC}|C&^u=p7A9X zure7K7SG~vT}Btue15Cys5U1*koeFA@BV{C#x6$gn)sSkI{;!~r)&3L>K2ss@_qEqOMNUVu~KA;C!;o`xv z08CcSY0z8|kU!Bzd>PK$866U4#0`Doz&^QFoe|IoT*x1c z8h3i6YNv1>wb%hUBoKT8FcAdr+x>){lwPZ3)dtHjQz#bnf7}0q8X5z$iA9?EqjG79 z0_gRm2I&aQSQ0yqqy$7%O6Y%D={LGQnK1;=V$Bneg&I{(pd7K6 zJjP`dK=mkoRHo{r%=XEroSy}Ggo~x&D&mTw(^?@Rsy~8sc!a0ST8f< z&!T{Usn8kLs7e&0e}eGPibTKSXiCbh( zVfToNM7KAL6)=L$P+)Jgx7T`=xD3N$u?d#oqT(nei3yamJ*)@7@_vYmE#x_(NTdhb z6=Y|BVpo6_46APKIvY`K_ajOTqOxiIhK(NV_gI!6FPy_Av;Y?dCzYSXx5P3m?kPea ziML?h4Di(T$jqOz$9&`jyBjXq?4^$Zly6?(D^pd_`=AGdo8+Z6KG;F zEcr1r(_GlPyz|1C2{hxJA)PXz1CBrhfFj7v5?i=x!$%KWxssv@yt~c`;K(+V?qJ-R zfz_#F5vw9LmRix~Nzs6hgB8b?OW2?pLZl5OU=v?sEWS4&#~67HZ7D5~HFr?DFUrWe zsJT|NS8KRv7iCP}Iew6mQ|OX)cliJ+RPR*(tZZrX7*|}Wf!!Qn1YG3x6;z7x2z*3= zc$U1#kg~_p2}D0NEonqa=Xp`31-Y4knD;HvLf=(?kz!^7EhH6;CoT6hDy1wYsAJZ_j3{QwZrl?l7)Q;_@U_N`(x zl~$p0E|i05*tp3JL~dUUDA6CH{ScU@npfMDg6C<)nWhDBQPkrU5c>AB*L(DL3hj+- zf6zKU_HoTtRya6jcl6C+UmhzHkiodnVRcKpj;!o#Ont?s<{@!LZZ?KcOww0#H?1l36)c#(DU|?{QsKr^?BU9zAOP z3c7pTD0>{Iw(*lfk6zqEX7K87xWy)R?)0sw+E9?zk78WN4V-oHs$hBnS}HLmuhO8} z@8z4fc(S$cc={RDduMUP%Cy0OAKYWl{^gz4dc*6i`HHLU&|P2lVuLC5 zmmFFku?%Z`ykJ__@adO#4!`=@(-i=ONOW2SFc6VeU$D;3Q5`DO?QJ*QY)2mYv8&E; z$1ph32ai3;O@oo)u2C_e{l?eZq{_tfefx{5an&e&mE6SQ(g?JG8vuo=U5f1~cs?^J zRl0Hz*S!BDDw!M=@p}P#V_cF|kS_=zA#&|i@^$AQ{;Ah)CkcAhZMR1=ln!TS&pCT3 zzNzE?OL3qa9wV`Ug{(n0+KUBBa%hHEepdhwLH0?3LQzArf8mMYVF#NcB`!l|BVc10 zFbPMsJt;B%Z!MZirO@78kEyQJkhQ$y4{Y?nUNto_?>SkXR*Qa1l{G-(dZu*21xere#(K1yvM15KF?=Fd&LhGMj1nB z!2(zyv4EA*Qnb_4lbLK$#K5!`b-kW4A1N|0CIEwm{)&nTFdzPgtYFW7{fFoLoui~^ zFG)>?KBd4DPjhzPVyNzCPnKb<(R--h<> z^|CS0H`p6)B!PYOUWXstYQy?`of5P?@P4pMqk<8V&NJmPtYT7y08zP{d=^@6Ex@Ia zFKjEZfHgKYHdS1lp2{a<=JqmLy51bD+^~`u2pGziH#nPt?NH*l`wRbK7k%a*tnbOk zJqNK&0e2YIx_7?cL5UeW*ceO97bvY}b~0B(_A)XIT>!_dUsdZlw(-LsbC3e8NT!|~ z9F#z{))%iB5(qY3^J;fbk843!hf&5x7XmDFVxAbSW1`IpZOJT<7f*u32Hjy;x+lmf z2`yI^;4vEic%Kmf_`(p`V~nbHds)j0UlOcDd4val_+4j30MUei2KfVQEG2M*5lyfl zs(=Y4Jg@Pi8a9N>=aK|%PaUi7pKiD3zV&r`>hqto(f&TGId_e9zUi&eq7wo{_AMZ5 zD86;FEDPBM$Bg^Vl{wDterCa6`XNk1?a?O zVz}l9#L!sw%afSZ@2T(3 zl4Pt+h?tZa$k1|bfxH1LzIkF9mSzcce@%rebkWzR06@xg;#Sn1>iOE2tmU@%i9wB8 zzZQ9z%DQ;0S4d#H_uF5wqIK)Nq5^aIA6c5ljoM)l492|ZDT_l!arPG5R!wO!&0?z6S zb`h8mq)?-x=&~(W*tl2}vx5`EgSPX7@3JR9e!JJGLL6Ft;pK7(pK*6l5qT@!bh#b< z-d#3$Xur=O4s{)L7mrw%&)ji>)V~Wm_3wUFCxsO^sWv=|M~q9i1zUneQ3|=|f9|&` zRfVW*ZM0)Q+irc2|H7&ixF?6OQa}b6;*P@1j%oWH?|YkdZ@t$SfDI}a&f-$F{K(f$ zEMN@`6wWA&6$b*B>MWfl{c0Xh45(O(q*h;enN1w+l1r$%SBfu{UwO6F-TZc|)-v%W zT1&9;nj4jftQBq)K19@6D3@&dM4w9vSaWdTiCZF!4~jqBD=SiP{@mW zEy;i-hGnr|omj+Q@YS-!J4W|(#H?`FT(GIvbZNO#W+pt|L@<10>kl2sn1KVtN;ETA zEc}{O+{p6*6#y?%z#K4%Kc{b2xAFSxxPMKZ}Fpp2xkO9 zigTgkA|}zJGcT;$SAx{gU&vEupJ-9Gnz|m!n3M-+1DA?(9mVk zrIXNapIE>m=&oxsFHk>u=T`ZD8WbQx(0ferCgN4NU1gFd3_SX%FRnVQg&!(!LbaY< zdP|iWh$St*_1!jfV4q79#&+zms`hgo@BkW8YgovBQYkus;AnrZ$EMOH#U9ijd}fc= zf%m*boj^VZ=m5TjP_HFpb!YnX7ogYDW7Q!)?m6K}~tD2UC;n+_fv`Q`g)gdhu!ckm70`V}LN>%=3 zVN+sO%TzI;dCS$Btt<6qwvIjeuo9a`-0dr_sj*{^MGJ3o%)^o@q^ql~QZs(cD6%jr z%h*>mwK`CBYxSLRjjKZJs!XMraSdm5OlAmK577Zt+Ag$SLx&Gp%hlIe!{%3cu%8ql z0`!fSZU)}$!1wO9nehQj*GO8Ck?7P}T(ZtYl1Bq$RqmTS?@S^8R=oHtCr%9QYH4Xn z9XWC&u|y$>^bgiIy?*`r;=#dzAIq3qGMQ-}>I%n(c19N9<`s*9$*g2qOi-I--R27Y?-oWQl#{*QbCTCuHWd;(_D8PnN ze&E?XHr98{TCaJH^=sy_XXhiiTc)yIs3>B{thUv4ADRrrX@eD^tsL zJ+Az|c8^IRAHdCnS$X;kkwWc8z#IJ&z~u{@dSWy2r-*@hfSmpXb{qwH1I)~uK$5=$ zn1G?rBBEH=hEejeK2rZva_!j8T}QnKtN=`20W|hw9Yb7N+`zi?*ISQfA@925gLZCn zqXeuqI-CY901WIZGX4C1!>}>}tg)=;GV_~<4t3w1bihioo8>VqzwhRECc@TO0KiZX zyi+MKC-?8MJ@OJ z*#q{o+p(=c17MmN6DR>5YCN%-VdTBbBLFb9I9TpLrkO!L*XolRgN{8XI58>aH3J9= zAg2H@O#}=`|F!?V@7Q^3&zEdMEBlE3(N6#?H*GF{K?)Mz5)D{f;#mtucS7RLnVQmLnx)!LK){CECSv1*ME(xn6B4`O1k(T<5vnc3M;mhsG<1{CuSKJ1tMt}rt7 z14scLE@99%C1PB@vNW4zfov9B05X6oyN`cPf6z4=a^4x%EE3Jx8L(m$vp~iBY?}*kfSZ2GIy=02DOF4ZSjC1~ z+-q!R^)jV;nJS5q{FZRQvPr+LlW)glk5E@80q_##_&Ix761c(y5yMK$)hvMDvqrf=;rsL)84hT=L9G|^ejwP2cofm7VB^t0)9ywOJ=lDdOrQN+0 z?8F1%#GoQ3lryk8OHbKSe{Fx3sZ^OXS$P3iN$1x~5o%fLYasNG1!f=K!5n#DfpNtN z^J11T(SRlOZD3e=aJ7da=B)){LNHcRl5vJrQc@ECEs87rT{-vLUKPJ%v2t7kfx&=5# zQLsg`@q>Dk7v4<5V^~Q)>0x}q0{Lu#nXKw$2EdA2qMtSi2dpql7+*!UGI5o4BRLts8)pl2HrNSEh#KxTc zd;xujU0xasaE`)xdTDIEprZ(+GO1KyDw-fEe3(v>p7_id=afz3ZDN{()0#Cy4-E}f zD&KLYN9B`tYjo@ML!c1071GpXgd{dC(KcB`JuH@-4lQXG@CS*`O Date: Tue, 5 Mar 2024 18:24:52 -0800 Subject: [PATCH 06/18] bump(ci): use latest codeql action this is an additional (useful) commit to ensure the previous acceptance is not brittle to checksum changes --- .github/workflows/codeql-analysis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 35b35604..0d688327 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -39,11 +39,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -54,7 +54,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -68,4 +68,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 From 3dff8b72257f4ea5bfe37f2015a55ad00a5d4fd7 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Tue, 5 Mar 2024 18:52:00 -0800 Subject: [PATCH 07/18] fix(cargo): fix the "bake_configurations" field "embed_paths" the config field should match that this is not adding dirs but files --- README.md | 2 +- pkg/cargo/kilnfile.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 613b0c69..2070c2e0 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ These are the mappings from bake flag to each field in a bake_configurations ele | `"properties_directories"` | `--properties-directory=` | This may be a list of directories. | | `"runtime_configurations_directories"` | `--runtime-configs-directory=` | This may be a list of directories. | | `"bosh_variables_directories"` | `--bosh-variables-directory=` | This may be a list of directories. | -| `"embed_paths_directories"` | `--embed=` | This may be a list of directories. | +| `"embed_files"` | `--embed=` | This may be a list of filepaths. | | `"variable_files"` | `--variables-file=` | This may be a list of filepaths. | ### The Lock File [(source)](https://pkg.go.dev/github.com/pivotal-cf/kiln/pkg/cargo#Kilnfile) diff --git a/pkg/cargo/kilnfile.go b/pkg/cargo/kilnfile.go index 6594797c..4622543e 100644 --- a/pkg/cargo/kilnfile.go +++ b/pkg/cargo/kilnfile.go @@ -368,6 +368,6 @@ type BakeConfiguration struct { PropertyDirectories []string `yaml:"properties_directories,omitempty" json:"properties_directories,omitempty"` RuntimeConfigDirectories []string `yaml:"runtime_configurations_directories,omitempty" json:"runtime_configurations_directories,omitempty"` BOSHVariableDirectories []string `yaml:"bosh_variables_directories,omitempty" json:"bosh_variables_directories,omitempty"` - EmbedPaths []string `yaml:"embed_paths_directories,omitempty" json:"embed_paths_directories,omitempty"` + EmbedPaths []string `yaml:"embed_paths,omitempty" json:"embed_paths,omitempty"` VariableFiles []string `yaml:"variable_files,omitempty" json:"variable_files,omitempty"` } From 68f5fbe8491b5fbbfe152df91e51593abee0411b Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Wed, 6 Mar 2024 10:28:31 -0800 Subject: [PATCH 08/18] bump crypto pkg --- go.mod | 6 +++--- go.sum | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index be7a9852..7231421f 100644 --- a/go.mod +++ b/go.mod @@ -33,10 +33,10 @@ require ( github.com/pivotal-cf/om v0.0.0-20230707145702-e2ef8fd451b1 github.com/snabb/httpreaderat v1.0.1 github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.19.0 + golang.org/x/crypto v0.21.0 golang.org/x/oauth2 v0.16.0 golang.org/x/sync v0.6.0 - golang.org/x/term v0.17.0 + golang.org/x/term v0.18.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -121,7 +121,7 @@ require ( go.opentelemetry.io/otel/trace v1.24.0 // indirect golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.21.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.18.0 // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/go.sum b/go.sum index 19035109..bf9bbbe6 100644 --- a/go.sum +++ b/go.sum @@ -633,6 +633,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -804,6 +806,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -813,6 +817,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 055d597b24805c39eb4146343d1c9248d2c2fefb Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Wed, 6 Mar 2024 16:11:12 -0800 Subject: [PATCH 09/18] upgrade workflow actions we no longer have submodules and we should parse the Go version from the mod file --- .github/workflows/create-debugging-artifact.yml | 6 +++--- .github/workflows/release.yml | 8 +++----- .github/workflows/test.yml | 11 ++++------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/.github/workflows/create-debugging-artifact.yml b/.github/workflows/create-debugging-artifact.yml index 011b7e01..ca3c0c47 100644 --- a/.github/workflows/create-debugging-artifact.yml +++ b/.github/workflows/create-debugging-artifact.yml @@ -19,16 +19,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 submodules: 'recursive' token: ${{ secrets.RELEEN_GITHUB_TOKEN }} - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version-file: go.mod check-latest: true - name: Build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 386b37e9..434a55b5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,17 +15,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - submodules: 'recursive' token: ${{ secrets.RELEEN_GITHUB_TOKEN }} - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: - go-version: "1.21" - check-latest: true + go-version-file: go.mod - name: Build run: go build ./... diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 82b97029..4a99a174 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,8 +1,7 @@ name: test on: push: - branches: - - main + branches: ["main"] pull_request: jobs: test: @@ -10,17 +9,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - submodules: 'recursive' token: ${{ secrets.RELEEN_GITHUB_TOKEN }} - name: Set up Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: - go-version: "1.21" - check-latest: true + go-version-file: go.mod - name: golangci-lint uses: golangci/golangci-lint-action@v3 From ac1295d16c5ed6b9f2ee9fad0616a32b97dc15c0 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Thu, 7 Mar 2024 10:35:35 -0800 Subject: [PATCH 10/18] update shas I am not sure what is causing this difference --- internal/acceptance/workflows/baking_a_tile.feature | 4 ++-- .../testdata/tiles/bake-record/tile/bake_records/0.1.0.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/acceptance/workflows/baking_a_tile.feature b/internal/acceptance/workflows/baking_a_tile.feature index 61282f68..d5a44fa1 100644 --- a/internal/acceptance/workflows/baking_a_tile.feature +++ b/internal/acceptance/workflows/baking_a_tile.feature @@ -16,8 +16,8 @@ Feature: As a developer, I want to bake a tile And "bake_records/0.2.0-dev.json" contains substring: "source_revision": "bc3ac24e192ba06a2eca19381ad785ec7069e0d0" And "bake_records/0.2.0-dev.json" contains substring: "tile_directory": "." And "bake_records/0.2.0-dev.json" contains substring: "kiln_version": "0.0.0+acceptance-tests" - And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "25a0bac10db840b33f2e281e7bb82627ce6c8f8d7c157af7d41d1e6d45d0cbd0" - And "tile-0.2.0-dev.pivotal" has sha256 sum "25a0bac10db840b33f2e281e7bb82627ce6c8f8d7c157af7d41d1e6d45d0cbd0" + And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" + And "tile-0.2.0-dev.pivotal" has sha256 sum "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" Scenario: it reads directory configuration from Kilnfile Given I have a tile source directory "testdata/tiles/non-standard-paths" diff --git a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json index 075e189d..1c2cdfce 100644 --- a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json +++ b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json @@ -3,5 +3,5 @@ "version": "0.1.0", "kiln_version": "0.0.0+acceptance-tests", "tile_directory": "tile", - "file_checksum": "9ef9c2dfcc87688a35d880bfc9141b724a8d61e6de0bbf2b16d226ac57182a98" + "file_checksum": "8800f6533aa968f517de292a5297af034d451485e22f79203ee8042a1ef47803" } \ No newline at end of file From 78ce8915d0e8db0c84246e1c76dfb7de7383c34c Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Thu, 7 Mar 2024 15:21:23 -0800 Subject: [PATCH 11/18] run gofumpt to get cleaner imports --- internal/commands/bake_test.go | 3 ++- internal/commands/rebake.go | 8 +++++--- pkg/bake/record_test.go | 12 ++++++------ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/internal/commands/bake_test.go b/internal/commands/bake_test.go index f94cbf29..7b02eff4 100644 --- a/internal/commands/bake_test.go +++ b/internal/commands/bake_test.go @@ -2,7 +2,6 @@ package commands_test import ( "errors" - "github.com/pivotal-cf/kiln/pkg/bake" "log" "os" "path/filepath" @@ -11,6 +10,8 @@ import ( "testing" "time" + "github.com/pivotal-cf/kiln/pkg/bake" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/pivotal-cf-experimental/gomegamatchers" diff --git a/internal/commands/rebake.go b/internal/commands/rebake.go index 53194b2c..53b87608 100644 --- a/internal/commands/rebake.go +++ b/internal/commands/rebake.go @@ -3,13 +3,15 @@ package commands import ( "encoding/json" "fmt" - "github.com/pivotal-cf/jhanda" - "github.com/pivotal-cf/kiln/internal/builder" - "github.com/pivotal-cf/kiln/pkg/bake" "log" "os" "path/filepath" "strings" + + "github.com/pivotal-cf/jhanda" + + "github.com/pivotal-cf/kiln/internal/builder" + "github.com/pivotal-cf/kiln/pkg/bake" ) type ReBake struct { diff --git a/pkg/bake/record_test.go b/pkg/bake/record_test.go index 0ea8d2a8..9d28266c 100644 --- a/pkg/bake/record_test.go +++ b/pkg/bake/record_test.go @@ -1,16 +1,16 @@ package bake_test import ( - "github.com/pivotal-cf/kiln/pkg/bake" - "github.com/stretchr/testify/assert" "os" "os/exec" "path/filepath" "testing" - "github.com/pivotal-cf/kiln/internal/builder" - + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/pivotal-cf/kiln/internal/builder" + "github.com/pivotal-cf/kiln/pkg/bake" ) func TestNewRecordFromFile(t *testing.T) { @@ -228,7 +228,7 @@ func TestBakeRecord_SetTileDirectory(t *testing.T) { repoRoot := createAndMoveToTemporaryTileDirectory(t) createGitRepository(t, repoRoot) subDir := filepath.Join("peach", "pear") - require.NoError(t, os.MkdirAll(subDir, 0766)) + require.NoError(t, os.MkdirAll(subDir, 0o766)) record, err := bake.Record{}.SetTileDirectory(subDir) require.NoError(t, err) @@ -260,7 +260,7 @@ func createAndMoveToTemporaryTileDirectory(t *testing.T, subDirectory ...string) if len(subDirectory) > 0 { subDir := filepath.Join(subDirectory...) - require.NoError(t, os.MkdirAll(subDir, 0766)) + require.NoError(t, os.MkdirAll(subDir, 0o766)) require.NoError(t, os.Chdir(subDir)) } From 06fc6287cfed4afd319e9e8b4ddfa05be750c1d5 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Fri, 8 Mar 2024 11:39:35 -0800 Subject: [PATCH 12/18] Revert "update shas" This reverts commit ac1295d16c5ed6b9f2ee9fad0616a32b97dc15c0. --- internal/acceptance/workflows/baking_a_tile.feature | 4 ++-- .../testdata/tiles/bake-record/tile/bake_records/0.1.0.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/acceptance/workflows/baking_a_tile.feature b/internal/acceptance/workflows/baking_a_tile.feature index d5a44fa1..61282f68 100644 --- a/internal/acceptance/workflows/baking_a_tile.feature +++ b/internal/acceptance/workflows/baking_a_tile.feature @@ -16,8 +16,8 @@ Feature: As a developer, I want to bake a tile And "bake_records/0.2.0-dev.json" contains substring: "source_revision": "bc3ac24e192ba06a2eca19381ad785ec7069e0d0" And "bake_records/0.2.0-dev.json" contains substring: "tile_directory": "." And "bake_records/0.2.0-dev.json" contains substring: "kiln_version": "0.0.0+acceptance-tests" - And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" - And "tile-0.2.0-dev.pivotal" has sha256 sum "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" + And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "25a0bac10db840b33f2e281e7bb82627ce6c8f8d7c157af7d41d1e6d45d0cbd0" + And "tile-0.2.0-dev.pivotal" has sha256 sum "25a0bac10db840b33f2e281e7bb82627ce6c8f8d7c157af7d41d1e6d45d0cbd0" Scenario: it reads directory configuration from Kilnfile Given I have a tile source directory "testdata/tiles/non-standard-paths" diff --git a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json index 1c2cdfce..075e189d 100644 --- a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json +++ b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json @@ -3,5 +3,5 @@ "version": "0.1.0", "kiln_version": "0.0.0+acceptance-tests", "tile_directory": "tile", - "file_checksum": "8800f6533aa968f517de292a5297af034d451485e22f79203ee8042a1ef47803" + "file_checksum": "9ef9c2dfcc87688a35d880bfc9141b724a8d61e6de0bbf2b16d226ac57182a98" } \ No newline at end of file From 84716420600fe377d8c5d636a431f986720f5ded Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Fri, 8 Mar 2024 11:44:24 -0800 Subject: [PATCH 13/18] fix(acceptance): not sure why shas changed but these are correct --- internal/acceptance/workflows/baking_a_tile.feature | 4 ++-- .../testdata/tiles/bake-record/tile/bake_records/0.1.0.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/acceptance/workflows/baking_a_tile.feature b/internal/acceptance/workflows/baking_a_tile.feature index 61282f68..d5a44fa1 100644 --- a/internal/acceptance/workflows/baking_a_tile.feature +++ b/internal/acceptance/workflows/baking_a_tile.feature @@ -16,8 +16,8 @@ Feature: As a developer, I want to bake a tile And "bake_records/0.2.0-dev.json" contains substring: "source_revision": "bc3ac24e192ba06a2eca19381ad785ec7069e0d0" And "bake_records/0.2.0-dev.json" contains substring: "tile_directory": "." And "bake_records/0.2.0-dev.json" contains substring: "kiln_version": "0.0.0+acceptance-tests" - And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "25a0bac10db840b33f2e281e7bb82627ce6c8f8d7c157af7d41d1e6d45d0cbd0" - And "tile-0.2.0-dev.pivotal" has sha256 sum "25a0bac10db840b33f2e281e7bb82627ce6c8f8d7c157af7d41d1e6d45d0cbd0" + And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" + And "tile-0.2.0-dev.pivotal" has sha256 sum "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" Scenario: it reads directory configuration from Kilnfile Given I have a tile source directory "testdata/tiles/non-standard-paths" diff --git a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json index 075e189d..1c2cdfce 100644 --- a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json +++ b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json @@ -3,5 +3,5 @@ "version": "0.1.0", "kiln_version": "0.0.0+acceptance-tests", "tile_directory": "tile", - "file_checksum": "9ef9c2dfcc87688a35d880bfc9141b724a8d61e6de0bbf2b16d226ac57182a98" + "file_checksum": "8800f6533aa968f517de292a5297af034d451485e22f79203ee8042a1ef47803" } \ No newline at end of file From 28a2574a0366a1a80617ecfc07420c95059602c3 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Fri, 8 Mar 2024 15:01:53 -0800 Subject: [PATCH 14/18] refactor: fix the rebake help docs --- internal/commands/rebake.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/commands/rebake.go b/internal/commands/rebake.go index 53b87608..e5aa29ad 100644 --- a/internal/commands/rebake.go +++ b/internal/commands/rebake.go @@ -82,7 +82,7 @@ func (cmd ReBake) Execute(args []string) error { func (cmd ReBake) Usage() jhanda.Usage { return jhanda.Usage{ - Description: "re-bake (aka record bake) builds a tile from a bake record if the current HEAD is does not match the record the command will fail", + Description: "re-bake (aka record bake) builds a tile from a bake record. You must check out the repository to the revision of the source_revision in the bake record before running this command.", ShortDescription: "re-bake constructs a tile from a bake record", Flags: &cmd.Options, } From ae535f08d3964b11d620318392e3051afb1fe9e9 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Fri, 8 Mar 2024 18:03:07 -0800 Subject: [PATCH 15/18] fix(acceptance): use a more standard git commit time format i think this might be an issue with the sha256 of the tile I would think it would also change the source_revision but maybe not --- .../workflows/scenario/step_funcs_tile_source_code.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go b/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go index 4f0afb86..2dabfd7c 100644 --- a/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go +++ b/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go @@ -131,8 +131,8 @@ func copyTileDirectory(dir, tileDirectory string) (string, error) { if err := executeAndWrapError(testTileDir, env, "git", "add", "."); err != nil { return "", fmt.Errorf("tile path is not a repository: adding initial files failed: %w", err) } - env = updateEnvVar(env, "GIT_AUTHOR_DATE", "1112937193 -0700") - env = updateEnvVar(env, "GIT_COMMITTER_DATE", "1112937193 -0700") + env = updateEnvVar(env, "GIT_AUTHOR_DATE", "Thu, 07 Apr 2005 22:13:13") + env = updateEnvVar(env, "GIT_COMMITTER_DATE", "Thu, 07 Apr 2005 22:13:13") if err := executeAndWrapError(testTileDir, env, "git", "commit", "-m", "initial commit"); err != nil { return "", fmt.Errorf("tile path is not a repository: adding initial files failed: %w", err) } From 1edf2610ad45f848ff5ed194c191c8dedb5ab558 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Sat, 9 Mar 2024 14:05:06 -0800 Subject: [PATCH 16/18] refactor(acceptance): remove un-needed TestMain function this adds confusion not sure why it is still here. The tests pass without it. --- internal/acceptance/workflows/acceptance_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/internal/acceptance/workflows/acceptance_test.go b/internal/acceptance/workflows/acceptance_test.go index a23c94fb..d3383d37 100644 --- a/internal/acceptance/workflows/acceptance_test.go +++ b/internal/acceptance/workflows/acceptance_test.go @@ -25,12 +25,6 @@ import ( "github.com/pivotal-cf/kiln/internal/acceptance/workflows/scenario" ) -func TestMain(m *testing.M) { - code := m.Run() - _ = exec.Command("git", "submodule", "update", "--init", "--recursive", "hello-tile").Run() - os.Exit(code) -} - func Test_baking_a_tile(t *testing.T) { // t.SkipNow() setupAndRunFeatureTest(t) From c1f61dd7a676e60909c68cb58d97261bbd748c36 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Thu, 14 Mar 2024 12:31:41 -0700 Subject: [PATCH 17/18] fix: timezone issue in zip headers use unix epoc as modified time Co-authored-by: Ramkumar Vengadakrishnan --- internal/acceptance/bake/bake_test.go | 4 +++ .../workflows/baking_a_tile.feature | 4 +-- internal/builder/metadata_git_sha.go | 31 ------------------- internal/commands/bake.go | 6 ++-- 4 files changed, 8 insertions(+), 37 deletions(-) diff --git a/internal/acceptance/bake/bake_test.go b/internal/acceptance/bake/bake_test.go index d3ad1f6a..089af17d 100644 --- a/internal/acceptance/bake/bake_test.go +++ b/internal/acceptance/bake/bake_test.go @@ -151,6 +151,10 @@ var _ = Describe("bake command", func() { file, err := bakedTile.Open("metadata/metadata.yml") Expect(err).NotTo(HaveOccurred()) + info, err := file.Stat() + Expect(err).NotTo(HaveOccurred()) + Expect(info.ModTime()).To(Equal(time.Unix(0, 0).In(time.UTC))) + metadataContents, err := io.ReadAll(file) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/acceptance/workflows/baking_a_tile.feature b/internal/acceptance/workflows/baking_a_tile.feature index d5a44fa1..711678f6 100644 --- a/internal/acceptance/workflows/baking_a_tile.feature +++ b/internal/acceptance/workflows/baking_a_tile.feature @@ -16,8 +16,8 @@ Feature: As a developer, I want to bake a tile And "bake_records/0.2.0-dev.json" contains substring: "source_revision": "bc3ac24e192ba06a2eca19381ad785ec7069e0d0" And "bake_records/0.2.0-dev.json" contains substring: "tile_directory": "." And "bake_records/0.2.0-dev.json" contains substring: "kiln_version": "0.0.0+acceptance-tests" - And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" - And "tile-0.2.0-dev.pivotal" has sha256 sum "3ac44ecc0215677ddde5e3d12df7c8ea7ac6e9ade0bf3c957bbed6645edf6811" + And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "98239fa2fad3132c4cc12407f0f6c77bdcae1faec00fb4ef4c2b420637522db4" + And "tile-0.2.0-dev.pivotal" has sha256 sum "98239fa2fad3132c4cc12407f0f6c77bdcae1faec00fb4ef4c2b420637522db4" Scenario: it reads directory configuration from Kilnfile Given I have a tile source directory "testdata/tiles/non-standard-paths" diff --git a/internal/builder/metadata_git_sha.go b/internal/builder/metadata_git_sha.go index a55cb825..20fb11bf 100644 --- a/internal/builder/metadata_git_sha.go +++ b/internal/builder/metadata_git_sha.go @@ -5,9 +5,7 @@ import ( "fmt" "os" "os/exec" - "strconv" "strings" - "time" ) const DirtyWorktreeSHAValue = "DEVELOPMENT" @@ -25,21 +23,6 @@ func GitMetadataSHA(repositoryDirectory string, isDev bool) (string, error) { return gitHeadRevision(repositoryDirectory) } -func ModifiedTime(repositoryDirectory string, isDev bool) (time.Time, error) { - if isDev { - return time.Now(), nil - } - if err := ensureGitExecutableIsFound(); err != nil { - return time.Time{}, err - } - if dirty, err := GitStateIsDirty(repositoryDirectory); err != nil { - return time.Time{}, err - } else if dirty && isDev { - return time.Now(), nil - } - return GitCommitterCommitDate(repositoryDirectory) -} - func GitStateIsDirty(repositoryDirectory string) (bool, error) { gitStatus := exec.Command("git", "status", "--porcelain") gitStatus.Dir = repositoryDirectory @@ -53,20 +36,6 @@ func GitStateIsDirty(repositoryDirectory string) (bool, error) { return false, nil } -func GitCommitterCommitDate(repositoryDirectory string) (time.Time, error) { - cmd := exec.Command("git", "show", "-s", "--format=%ct") - cmd.Dir = repositoryDirectory - output, err := cmd.Output() - if err != nil { - return time.Time{}, err - } - commitTime, err := strconv.ParseInt(strings.TrimSpace(string(output)), 10, 64) - if err != nil { - return time.Time{}, err - } - return time.Unix(commitTime, 0), nil -} - func gitHeadRevision(repositoryDirectory string) (string, error) { var out bytes.Buffer gitRevParseHead := exec.Command("git", "rev-parse", "HEAD") diff --git a/internal/commands/bake.go b/internal/commands/bake.go index 61bfc7eb..10871584 100644 --- a/internal/commands/bake.go +++ b/internal/commands/bake.go @@ -13,6 +13,7 @@ import ( "path/filepath" "slices" "strings" + "time" "github.com/go-git/go-billy/v5" "github.com/pivotal-cf/jhanda" @@ -532,10 +533,7 @@ func (b Bake) Execute(args []string) error { return fmt.Errorf("failed to read metadata: %s", err) } - modTime, err := builder.ModifiedTime(filepath.Dir(b.Options.Kilnfile), isDevBuild) - if err != nil { - return fmt.Errorf("failed to read modified date from commit: %s", err) - } + modTime := time.Unix(0, 0).In(time.UTC) input := builder.InterpolateInput{ Version: b.Options.Version, From d61c86917a7b0976b53f6ede815ffb40f6dd8923 Mon Sep 17 00:00:00 2001 From: Christopher Hunter Date: Thu, 14 Mar 2024 14:47:25 -0700 Subject: [PATCH 18/18] refactor: rollback test time to reduce diff in PR this should makes the true diff in this PR easier to follow Co-authored-by: Ramkumar Vengadakrishnan --- internal/acceptance/workflows/baking_a_tile.feature | 4 ++-- .../workflows/scenario/step_funcs_tile_source_code.go | 4 ++-- .../testdata/tiles/bake-record/tile/bake_records/0.1.0.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/acceptance/workflows/baking_a_tile.feature b/internal/acceptance/workflows/baking_a_tile.feature index 711678f6..e346af95 100644 --- a/internal/acceptance/workflows/baking_a_tile.feature +++ b/internal/acceptance/workflows/baking_a_tile.feature @@ -16,8 +16,8 @@ Feature: As a developer, I want to bake a tile And "bake_records/0.2.0-dev.json" contains substring: "source_revision": "bc3ac24e192ba06a2eca19381ad785ec7069e0d0" And "bake_records/0.2.0-dev.json" contains substring: "tile_directory": "." And "bake_records/0.2.0-dev.json" contains substring: "kiln_version": "0.0.0+acceptance-tests" - And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "98239fa2fad3132c4cc12407f0f6c77bdcae1faec00fb4ef4c2b420637522db4" - And "tile-0.2.0-dev.pivotal" has sha256 sum "98239fa2fad3132c4cc12407f0f6c77bdcae1faec00fb4ef4c2b420637522db4" + And "bake_records/0.2.0-dev.json" contains substring: "file_checksum": "5f8abc7a3272a70fa716cdf120f6976f6b78e16a01a4b3e085ced7f51d6c7691" + And "tile-0.2.0-dev.pivotal" has sha256 sum "5f8abc7a3272a70fa716cdf120f6976f6b78e16a01a4b3e085ced7f51d6c7691" Scenario: it reads directory configuration from Kilnfile Given I have a tile source directory "testdata/tiles/non-standard-paths" diff --git a/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go b/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go index 2dabfd7c..4f0afb86 100644 --- a/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go +++ b/internal/acceptance/workflows/scenario/step_funcs_tile_source_code.go @@ -131,8 +131,8 @@ func copyTileDirectory(dir, tileDirectory string) (string, error) { if err := executeAndWrapError(testTileDir, env, "git", "add", "."); err != nil { return "", fmt.Errorf("tile path is not a repository: adding initial files failed: %w", err) } - env = updateEnvVar(env, "GIT_AUTHOR_DATE", "Thu, 07 Apr 2005 22:13:13") - env = updateEnvVar(env, "GIT_COMMITTER_DATE", "Thu, 07 Apr 2005 22:13:13") + env = updateEnvVar(env, "GIT_AUTHOR_DATE", "1112937193 -0700") + env = updateEnvVar(env, "GIT_COMMITTER_DATE", "1112937193 -0700") if err := executeAndWrapError(testTileDir, env, "git", "commit", "-m", "initial commit"); err != nil { return "", fmt.Errorf("tile path is not a repository: adding initial files failed: %w", err) } diff --git a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json index 1c2cdfce..205472bf 100644 --- a/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json +++ b/internal/acceptance/workflows/testdata/tiles/bake-record/tile/bake_records/0.1.0.json @@ -3,5 +3,5 @@ "version": "0.1.0", "kiln_version": "0.0.0+acceptance-tests", "tile_directory": "tile", - "file_checksum": "8800f6533aa968f517de292a5297af034d451485e22f79203ee8042a1ef47803" + "file_checksum": "781290c114d1e06f44e4bf05c22b5b03ee576f6526b28b103417050934f878b3" } \ No newline at end of file