Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions pkg/devcontainer/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ func (r *runner) extendImage(
}

// get extend image build info
extendedBuildInfo, err := feature.GetExtendedBuildInfo(
substitutionContext,
imageBuildInfo,
imageBase,
parsedConfig,
options.ForceBuild,
featureSecretOpts(options),
)
extendedBuildInfo, err := feature.GetExtendedBuildInfo(&feature.ExtendedBuildParams{
Ctx: substitutionContext,
ImageBuildInfo: imageBuildInfo,
Target: imageBase,
DevContainerConfig: parsedConfig,
ForceBuild: options.ForceBuild,
SecretOpts: featureSecretOpts(options),
})
if err != nil {
return nil, fmt.Errorf("get extended build info: %w", err)
}
Expand Down Expand Up @@ -153,14 +153,14 @@ func (r *runner) buildAndExtendImage(
}

// get extend image build info
extendedBuildInfo, err := feature.GetExtendedBuildInfo(
substitutionContext,
imageBuildInfo,
imageBase,
parsedConfig,
options.ForceBuild,
featureSecretOpts(options),
)
extendedBuildInfo, err := feature.GetExtendedBuildInfo(&feature.ExtendedBuildParams{
Ctx: substitutionContext,
ImageBuildInfo: imageBuildInfo,
Target: imageBase,
DevContainerConfig: parsedConfig,
ForceBuild: options.ForceBuild,
SecretOpts: featureSecretOpts(options),
})
if err != nil {
return nil, fmt.Errorf("get extended build info: %w", err)
}
Expand Down
16 changes: 8 additions & 8 deletions pkg/devcontainer/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -685,14 +685,14 @@ func (r *runner) buildAndExtendDockerCompose(
if featureSecretsFile != "" {
secretOpts = &feature.SecretOptions{SecretsFile: featureSecretsFile}
}
extendImageBuildInfo, err := feature.GetExtendedBuildInfo(
substitutionContext,
imageBuildInfo,
buildTarget,
parsedConfig,
false,
secretOpts,
)
extendImageBuildInfo, err := feature.GetExtendedBuildInfo(&feature.ExtendedBuildParams{
Ctx: substitutionContext,
ImageBuildInfo: imageBuildInfo,
Target: buildTarget,
DevContainerConfig: parsedConfig,
ForceBuild: false,
SecretOpts: secretOpts,
})
if err != nil {
return composeExtendResult{}, err
}
Expand Down
24 changes: 15 additions & 9 deletions pkg/devcontainer/feature/annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ import (
"github.com/stretchr/testify/require"
)

const (
testAnnotationTitle = "org.opencontainers.image.title"
testAnnotationDescription = "org.opencontainers.image.description"
testAnnotationVersion = "org.opencontainers.image.version"
)

func TestSaveAnnotations(t *testing.T) {
dir := t.TempDir()
annotations := map[string]string{
"org.opencontainers.image.title": "Go",
"org.opencontainers.image.description": "Installs Go and common Go tools",
"org.opencontainers.image.version": "1.2.3",
"org.opencontainers.image.source": "https://github.com/devcontainers/features",
testAnnotationTitle: "Go",
testAnnotationDescription: "Installs Go and common Go tools",
testAnnotationVersion: "1.2.3",
"org.opencontainers.image.source": "https://github.com/devcontainers/features",
}

saveAnnotations(dir, annotations)
Expand Down Expand Up @@ -47,8 +53,8 @@ func TestLoadOCIAnnotations_Present(t *testing.T) {
require.NoError(t, os.MkdirAll(extractedDir, 0o750))

annotations := map[string]string{
"org.opencontainers.image.title": "Node.js",
"org.opencontainers.image.description": "Installs Node.js and common npm tools",
testAnnotationTitle: "Node.js",
testAnnotationDescription: "Installs Node.js and common npm tools",
"org.opencontainers.image.authors": "Dev Containers",
"org.opencontainers.image.url": "https://github.com/devcontainers/features/tree/main/src/node",
"org.opencontainers.image.documentation": "https://containers.dev/features",
Expand Down Expand Up @@ -94,9 +100,9 @@ func TestLogOCIAnnotations_NoTitle(t *testing.T) {

func TestLogOCIAnnotations_WithTitle(t *testing.T) {
annotations := map[string]string{
"org.opencontainers.image.title": "Go",
"org.opencontainers.image.description": "Installs Go",
"org.opencontainers.image.version": "1.0.0",
testAnnotationTitle: "Go",
testAnnotationDescription: "Installs Go",
testAnnotationVersion: "1.0.0", //nolint:goconst
}
// Should not panic
logOCIAnnotations("ghcr.io/devcontainers/features/go:1", annotations)
Expand Down
2 changes: 1 addition & 1 deletion pkg/devcontainer/feature/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (s *CollectionTestSuite) TestFetchCollection_InvalidJSON() {
func (s *CollectionTestSuite) TestListCollectionFeatures() {
collection := Collection{
Features: []CollectionFeature{
{ID: "rust", Version: "1.0.0", Name: "Rust"},
{ID: "rust", Version: "1.0.0", Name: "Rust"}, //nolint:goconst
{ID: "python", Version: "3.0.0", Name: "Python"},
{ID: "java", Version: "1.5.0", Name: "Java"},
},
Expand Down
84 changes: 47 additions & 37 deletions pkg/devcontainer/feature/extend.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,22 @@ type BuildInfo struct {
BuildArgs map[string]string
}

func GetExtendedBuildInfo(
ctx *config.SubstitutionContext,
imageBuildInfo *config.ImageBuildInfo,
target string,
devContainerConfig *config.SubstitutedConfig,
forceBuild bool,
secretOpts *SecretOptions,
) (*ExtendedBuildInfo, error) {
type ExtendedBuildParams struct {
Ctx *config.SubstitutionContext
ImageBuildInfo *config.ImageBuildInfo
Target string
DevContainerConfig *config.SubstitutedConfig
ForceBuild bool
SecretOpts *SecretOptions
}

func GetExtendedBuildInfo(params *ExtendedBuildParams) (*ExtendedBuildInfo, error) {
ctx := params.Ctx
imageBuildInfo := params.ImageBuildInfo
target := params.Target
devContainerConfig := params.DevContainerConfig
forceBuild := params.ForceBuild
secretOpts := params.SecretOpts
features, err := fetchFeatures(devContainerConfig.Config, forceBuild, secretOpts)
if err != nil {
return nil, fmt.Errorf("fetch features: %w", err)
Expand Down Expand Up @@ -219,22 +227,22 @@ func getFeatureSafeID(featureID string) string {
}

func getFeatureLayers(containerUser, remoteUser string, features []*config.FeatureSet) string {
result := `RUN \
echo "_CONTAINER_USER_HOME=$(getent passwd ` + containerUser + ` | cut -d: -f6)" >> /tmp/build-features/devcontainer-features.builtin.env && \
echo "_REMOTE_USER_HOME=$(getent passwd ` + remoteUser + ` | cut -d: -f6)" >> /tmp/build-features/devcontainer-features.builtin.env
const envFile = "/tmp/build-features/devcontainer-features.builtin.env"
var b strings.Builder
b.WriteString("RUN \\\n")
b.WriteString(`echo "_CONTAINER_USER_HOME=$(getent passwd ` + containerUser)
b.WriteString(` | cut -d: -f6)" >> ` + envFile + " && \\\n")
b.WriteString(`echo "_REMOTE_USER_HOME=$(getent passwd ` + remoteUser)
b.WriteString(` | cut -d: -f6)" >> ` + envFile + "\n\n")

`
for i, feature := range features {
result += generateContainerEnvs(feature)
result += `
RUN cd /tmp/build-features/` + strconv.Itoa(i) + ` \
&& chmod +x ./devcontainer-features-install.sh \
&& ./devcontainer-features-install.sh

`
b.WriteString(generateContainerEnvs(feature))
b.WriteString("\nRUN cd /tmp/build-features/" + strconv.Itoa(i) + " \\\n")
b.WriteString("&& chmod +x ./devcontainer-features-install.sh \\\n")
b.WriteString("&& ./devcontainer-features-install.sh\n\n")
}

return result
return b.String()
}

func generateContainerEnvs(feature *config.FeatureSet) string {
Expand All @@ -253,7 +261,14 @@ func findContainerUsers(
baseImageMetadata *config.ImageMetadataConfig,
composeServiceUser, imageUser string,
) (string, string) {
reversed := config.ReverseSlice(baseImageMetadata.Config)
containerUser, remoteUser := usersFromMetadata(baseImageMetadata)
containerUser = applyUserFallback(containerUser, composeServiceUser, imageUser)
remoteUser = applyUserFallback(remoteUser, composeServiceUser, imageUser)
return containerUser, remoteUser
}

func usersFromMetadata(meta *config.ImageMetadataConfig) (string, string) {
reversed := config.ReverseSlice(meta.Config)
containerUser := ""
remoteUser := ""
for _, imageMetadata := range reversed {
Expand All @@ -264,21 +279,6 @@ func findContainerUsers(
remoteUser = imageMetadata.RemoteUser
}
}

if containerUser == "" {
if composeServiceUser != "" {
containerUser = composeServiceUser
} else if imageUser != "" {
containerUser = imageUser
}
}
if remoteUser == "" {
if composeServiceUser != "" {
remoteUser = composeServiceUser
} else if imageUser != "" {
remoteUser = imageUser
}
}
return containerUser, remoteUser
}

Expand All @@ -290,6 +290,16 @@ func ResolveFeatureOrder(
return fetchFeatures(devContainerConfig, false, nil)
}

func applyUserFallback(user, composeServiceUser, imageUser string) string {
if user != "" {
return user
}
if composeServiceUser != "" {
return composeServiceUser
}
return imageUser
}

func fetchFeatures(
devContainerConfig *config.DevContainerConfig,
forceBuild bool,
Expand Down Expand Up @@ -442,7 +452,7 @@ func (r *featureDependencyResolver) findByConfigID(configID string) (string, *co
return "", nil
}

func (r *featureDependencyResolver) resolveFeatureDependency(
func (r *featureDependencyResolver) resolveFeatureDependency( //nolint:cyclop
featureID string,
featureSet *config.FeatureSet,
) error {
Expand Down
Loading
Loading