All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jreleaser.model.internal.environment.Environment Maven / Gradle / Ivy

The newest version!
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * Copyright 2020-2024 The JReleaser authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jreleaser.model.internal.environment;

import com.fasterxml.jackson.annotation.JsonIgnore;
import org.jreleaser.bundle.RB;
import org.jreleaser.config.JReleaserConfigLoader;
import org.jreleaser.config.JReleaserConfigParser;
import org.jreleaser.model.internal.JReleaserContext;
import org.jreleaser.model.internal.common.AbstractModelObject;
import org.jreleaser.model.internal.common.Domain;
import org.jreleaser.util.Env;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeSet;

import static java.nio.file.Files.newInputStream;
import static java.util.Collections.unmodifiableMap;
import static org.jreleaser.model.Constants.DEFAULT_GIT_REMOTE;
import static org.jreleaser.model.Constants.JRELEASER_USER_HOME;
import static org.jreleaser.model.Constants.XDG_CONFIG_HOME;
import static org.jreleaser.util.Env.JRELEASER_ENV_PREFIX;
import static org.jreleaser.util.Env.JRELEASER_SYS_PREFIX;
import static org.jreleaser.util.Env.envKey;
import static org.jreleaser.util.StringUtils.getPropertyNameForLowerCaseHyphenSeparatedName;
import static org.jreleaser.util.StringUtils.isBlank;
import static org.jreleaser.util.StringUtils.isNotBlank;

/**
 * @author Andres Almiray
 * @since 0.1.0
 */
public final class Environment extends AbstractModelObject implements Domain {
    private static final long serialVersionUID = 4554098923129885325L;

    private final Map properties = new LinkedHashMap<>();
    @JsonIgnore
    private final Map sourcedProperties = new LinkedHashMap<>();
    @JsonIgnore
    private PropertiesSource propertiesSource;
    private String variables;
    @JsonIgnore
    private Properties vars;
    @JsonIgnore
    private Path propertiesFile;

    @JsonIgnore
    private final org.jreleaser.model.api.environment.Environment immutable = new org.jreleaser.model.api.environment.Environment() {
        private static final long serialVersionUID = -7287090119869371299L;

        @Override
        public Properties getVars() {
            return vars;
        }

        @Override
        public String getVariables() {
            return variables;
        }

        @Override
        public Map getProperties() {
            return unmodifiableMap(properties);
        }

        @Override
        public Map asMap(boolean full) {
            return unmodifiableMap(Environment.this.asMap(full));
        }
    };

    public org.jreleaser.model.api.environment.Environment asImmutable() {
        return immutable;
    }

    @Override
    public void merge(Environment source) {
        this.variables = merge(this.variables, source.variables);
        setProperties(merge(this.properties, source.properties));
        setPropertiesSource(merge(this.propertiesSource, source.propertiesSource));
    }

    public String resolve(String key) {
        return env(key, Env.sys(key, ""));
    }

    public String resolve(String key, String value) {
        return env(key, Env.sys(key, value));
    }

    public String resolveOrDefault(String key, String value, String defaultValue) {
        String result = env(key, Env.sys(key, value));
        return isNotBlank(result) ? result : defaultValue;
    }

    private String env(String key, String value) {
        if (isNotBlank(value)) {
            return value;
        }
        return getVariable(envKey(key));
    }

    public Properties getVars() {
        return vars;
    }

    public String getVariable(String key) {
        return vars.getProperty(Env.envKey(key));
    }

    public boolean isSet() {
        return isNotBlank(variables) ||
            !properties.isEmpty();
    }

    public PropertiesSource getPropertiesSource() {
        return propertiesSource;
    }

    public void setPropertiesSource(PropertiesSource propertiesSource) {
        this.propertiesSource = propertiesSource;
        if (null != this.propertiesSource) {
            sourcedProperties.putAll(propertiesSource.getProperties());
        }
    }

    public String getVariables() {
        return variables;
    }

    public void setVariables(String variables) {
        this.variables = variables;
    }

    public Map getProperties() {
        return properties;
    }

    public void setProperties(Map properties) {
        this.properties.putAll(properties);
    }

    public Map getSourcedProperties() {
        return sourcedProperties;
    }

    public Path getPropertiesFile() {
        return propertiesFile;
    }

    @Override
    public Map asMap(boolean full) {
        Map map = new LinkedHashMap<>();
        map.put("variables", variables);
        map.put("properties", properties);

        return map;
    }

    public void initProps(JReleaserContext context) {
        if (null == vars) {
            vars = new Properties();

            Path configDirectory = null;

            String home = System.getenv(XDG_CONFIG_HOME);
            if (isNotBlank(home) && Files.exists(Paths.get(home).resolve("jreleaser"))) {
                configDirectory = Paths.get(home).resolve("jreleaser");
            }

            if (null == configDirectory) {
                home = System.getenv(JRELEASER_USER_HOME);
                if (isBlank(home)) {
                    home = System.getProperty("user.home") + File.separator + ".jreleaser";
                }
                configDirectory = Paths.get(home);
            }

            loadVariables(context, resolveConfigFileAt(configDirectory)
                .orElse(configDirectory.resolve("config.properties")));

            if (isNotBlank(variables)) {
                loadVariables(context, context.getBasedir().resolve(variables.trim()));
            }

            Path envFilePath = context.getBasedir().resolve(".env");
            if (Files.exists(envFilePath)) {
                loadVariables(context, envFilePath);
            }

            // env vars
            Set keyNames = new TreeSet<>();
            Properties envVars = new Properties();
            System.getenv().forEach((k, v) -> {
                if (k.startsWith(JRELEASER_ENV_PREFIX)) keyNames.add(k);
                if (k.startsWith(JRELEASER_ENV_PREFIX)) envVars.put(k, v);
            });
            if (System.getenv().containsKey(envKey(DEFAULT_GIT_REMOTE))) {
                keyNames.add(envKey(DEFAULT_GIT_REMOTE));
            }
            if (!keyNames.isEmpty()) {
                context.getLogger().debug(RB.$("environment.variables.env"));
                keyNames.forEach(message -> context.getLogger().debug("  " + message));
            }

            // system props
            keyNames.clear();
            System.getProperties().stringPropertyNames().forEach(k -> {
                if (k.startsWith(JRELEASER_SYS_PREFIX)) keyNames.add(k);
            });
            if (!keyNames.isEmpty()) {
                context.getLogger().debug(RB.$("environment.system.properties"));
                keyNames.forEach(message -> context.getLogger().debug("  " + message));
            }

            // merge keyNames
            Properties merged = new Properties();
            merged.putAll(envVars);
            merged.putAll(this.vars);
            this.vars.clear();
            this.vars.putAll(merged);

            if (null != propertiesSource) {
                sourcedProperties.putAll(propertiesSource.getProperties());
            }
        }
    }

    private void loadVariables(JReleaserContext context, Path file) {
        propertiesFile = file;
        context.getLogger().info(RB.$("environment.load.variables"), file.toAbsolutePath());
        if (Files.exists(file)) {
            try {
                Properties p = new Properties();
                if (file.getFileName().toString().endsWith(".properties") ||
                    file.getFileName().toString().equals(".env")) {
                    try (InputStream in = newInputStream(file)) {
                        p.load(in);
                    }
                } else {
                    p.putAll(JReleaserConfigLoader.loadProperties(file));
                }
                vars.putAll(p);

                Set keyNames = new TreeSet<>();
                p.stringPropertyNames().stream()
                    .filter(k -> k.startsWith(JRELEASER_ENV_PREFIX)).
                    forEach(keyNames::add);

                if (!keyNames.isEmpty()) {
                    context.getLogger().debug(RB.$("environment.variables.file", file.getFileName().toString()));
                    keyNames.forEach(message -> context.getLogger().debug("  " + message));
                }
            } catch (IOException e) {
                context.getLogger().debug(RB.$("environment.variables.load.error"), file.toAbsolutePath(), e);
            }
        } else {
            context.getLogger().warn(RB.$("environment.variables.source.missing"), file.toAbsolutePath());
        }
    }

    private Optional resolveConfigFileAt(Path directory) {
        ServiceLoader parsers = ServiceLoader.load(JReleaserConfigParser.class,
            JReleaserConfigParser.class.getClassLoader());

        for (JReleaserConfigParser parser : parsers) {
            Path file = directory.resolve("config." + parser.getPreferredFileExtension());
            if (Files.exists(file)) {
                return Optional.of(file);
            }
        }

        return Optional.empty();
    }

    public boolean getBooleanProperty(String key) {
        boolean keyInProperties = properties.containsKey(key) && Boolean.parseBoolean(String.valueOf(properties.get(key)));
        boolean keyInSourcedProperties = sourcedProperties.containsKey(key) && Boolean.parseBoolean(String.valueOf(sourcedProperties.get(key)));
        return keyInProperties || keyInSourcedProperties;
    }

    public interface PropertiesSource extends Serializable {
        Map getProperties();
    }

    public abstract static class AbstractPropertiesSource implements PropertiesSource {
        private static final long serialVersionUID = 9102569253517657171L;

        @Override
        public Map getProperties() {
            Map props = doGetProperties();
            Map map = new LinkedHashMap<>();

            props.forEach((key, value) -> {
                if (key.startsWith("JRELEASER_")) return;
                String k = key.replace(".", "-");
                k = getPropertyNameForLowerCaseHyphenSeparatedName(k);
                map.put(k, value);
            });

            return map;
        }

        protected abstract Map doGetProperties();
    }

    public static class PropertiesPropertiesSource extends AbstractPropertiesSource {
        private static final long serialVersionUID = 7747477120107034027L;

        private final Properties properties;

        public PropertiesPropertiesSource(Properties properties) {
            this.properties = properties;
        }

        @Override
        protected Map doGetProperties() {
            Map map = new LinkedHashMap<>();
            properties.forEach((k, v) -> map.put(String.valueOf(k), String.valueOf(v)));
            return map;
        }
    }

    public static class MapPropertiesSource extends AbstractPropertiesSource {
        private static final long serialVersionUID = 6643212572356054605L;

        private final Map properties;

        public MapPropertiesSource(Map properties) {
            this.properties = properties;
        }

        @Override
        protected Map doGetProperties() {
            Map map = new LinkedHashMap<>();
            properties.forEach((k, v) -> map.put(k, String.valueOf(v)));
            return map;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy