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

net.oneandone.stool.cli.Create Maven / Gradle / Ivy

There is a newer version: 4.0.3
Show newest version
/**
 * Copyright 1&1 Internet AG, https://github.com/1and1/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.oneandone.stool.cli;

import net.oneandone.inline.ArgumentException;
import net.oneandone.stool.configuration.Property;
import net.oneandone.stool.configuration.StageConfiguration;
import net.oneandone.stool.locking.Mode;
import net.oneandone.stool.stage.ArtifactStage;
import net.oneandone.stool.stage.SourceStage;
import net.oneandone.stool.stage.Stage;
import net.oneandone.stool.util.Files;
import net.oneandone.stool.util.RmRfThread;
import net.oneandone.stool.util.Session;
import net.oneandone.sushi.fs.file.FileNode;
import net.oneandone.sushi.launcher.Launcher;
import net.oneandone.sushi.util.Separator;
import net.oneandone.sushi.util.Strings;

import java.io.IOException;
import java.nio.file.LinkOption;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class Create extends SessionCommand {
    private final boolean quiet;
    private String urlOrFileOrSearch;

    private FileNode directory;

    private final Map config = new LinkedHashMap<>();

    private StageConfiguration stageConfiguration;

    private final Map properties;

    public Create(Session session, boolean quiet, String urlOrFileOrSearch) {
        super(session, Mode.NONE);
        this.quiet = quiet;
        this.urlOrFileOrSearch = urlOrFileOrSearch;
        this.directory = null;
        this.properties = session.properties();
    }


    public void dirOrProperty(String str) {
        int idx;
        String key;
        String value;
        Property property;

        idx = str.indexOf('=');
        if (idx == -1) {
            if (directory == null) {
                directory = world.file(str);
                return;
            }
            throw new ArgumentException("Invalid configuration argument. Expected =, got " + str);
        }
        key = str.substring(0, idx);
        value = str.substring(idx + 1);
        property = properties.get(key);
        if (property == null) {
            throw new ArgumentException("unknown property: " + key);
        }
        if (config.put(property, value) != null) {
            throw new ArgumentException("already configured: " + key);
        }
    }

    @Override
    public void doRun() throws Exception {
        String url;
        RmRfThread cleanup;
        Stage stage;

        url = url();
        defaults(url);
        cleanup = new RmRfThread(console);
        cleanup.add(directory);
        Runtime.getRuntime().addShutdownHook(cleanup);

        // if this method fails with an exception or it is aborted with ctrl-c, the shutdown hook is used to wipe things
        stage = create(url);

        Runtime.getRuntime().removeShutdownHook(cleanup);

        session.add(stage.backstage, stage.getId());
        console.info.println("stage created: " + stage.getName());
        session.cd(stage.getDirectory());
    }

    private String url() throws IOException {
        FileNode file;
        String substring;
        List urls;
        String input;
        int no;

        file = world.file(urlOrFileOrSearch);
        if (file.isFile()) {
            return file.getUri().toString();
        }
        if (!urlOrFileOrSearch.startsWith("%")) {
            return Strings.removeRightOpt(urlOrFileOrSearch, "/");
        }
        console.info.println("Searching ...");
        substring = urlOrFileOrSearch.substring(1);
        urls = session.search(substring);
        if (urls.size() == 0) {
            throw new ArgumentException("not found: " + substring);
        }
        for (int i = 1; i <= urls.size(); i++) {
            console.info.println("[" + i + "] " + urls.get(i - 1));
        }
        while (true) {
            input = console.readline("Choose url [1-" + urls.size() + "]) or press ctrl-c to abort: \n");
            try {
                no = Integer.parseInt(input);
                return Strings.removeLeftOpt(urls.get(no - 1), "scm:");
            } catch (NumberFormatException e) {
                console.info.println("invalid input: " + e.getMessage());
            }
        }
    }

    private void defaults(String url) throws IOException {
        Property np;
        String name;
        FileNode surrounding;

        if (directory == null) {
            directory = world.getWorking().join(Stage.nameForUrl(url));
        }
        if (directory.isDirectory()) {
            throw new ArgumentException("stage directory already exists: " + directory);
        }
        surrounding = session.findStageDirectory(directory);
        if (surrounding != null) {
            throw new ArgumentException("cannot create a stage within a stage: " + directory.getAbsolute() + " in " + surrounding);
        }
        if (!directory.getParent().isDirectory()) {
            throw new ArgumentException("parent directory for new stage does not exist: " + directory.getParent());
        }
        if (session.configuration.shared) {
            checkPermissions(directory.getParent());
        }
        session.checkDiskFree();
        np = properties.get("name");
        name = config.get(np);
        if (name == null) {
            name = directory.getName();
            config.put(np, name);
        }
        Stage.checkName(name);
        if (session.stageNames().contains(name)) {
            throw new ArgumentException("stage name already exists: " + name);
        }
        if (stageConfiguration == null) {
            stageConfiguration = session.createStageConfiguration(url);
        }
    }

    private void checkPermissions(FileNode node) throws IOException {
        PosixFileAttributes attributes;
        FileNode parent;

        attributes = java.nio.file.Files.readAttributes(node.toPath(), PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
        if (!attributes.permissions().contains(PosixFilePermission.GROUP_EXECUTE)
                && !attributes.permissions().contains(PosixFilePermission.OTHERS_EXECUTE)) {
            throw new IOException(node.getAbsolute() + ": missing execute permission for group or others. \n" +
                    "You cannot create a stage in this directory because only you can access it.\n" +
                    "Try to create your stage in a different directory.");
        }
        parent = node.getParent();
        if (parent != null && !parent.equals(node)) {
            checkPermissions(parent);
        }
    }

    private Stage create(String url) throws Exception {
        Stage stage;
        String prepare;

        Files.createSourceDirectory(console.verbose, directory, session.group());
        stage = stage(url);
        // CAUTION: create backstage before possible prepare commands -- e.g. pws already populates the local repository of the stage
        Files.createStoolDirectory(console.verbose, stage.backstage);

        // make sure to run in stage environment, e.g. to have proper repository settings
        prepare = stage.config().prepare;
        if (!prepare.isEmpty()) {
            Launcher l = stage.launcher(Strings.toArray(Separator.SPACE.split(prepare)));
            if (quiet) {
                l.exec();
            } else {
                l.exec(console.info);
            }
        }
        stage.tuneConfiguration();
        for (Map.Entry entry : config.entrySet()) {
            entry.getKey().set(stage.config(), entry.getValue());
        }
        stage.initialize();
        return stage;
    }

    private Stage stage(String url) throws Exception {
        String id;
        ArtifactStage artifactStage;
        Stage stage;

        id = session.nextStageId();
        if (ArtifactStage.isArtifact(url)) {
            artifactStage = new ArtifactStage(session, url, id, directory, stageConfiguration);
            artifactStage.populateDirectory(console);
            stage = artifactStage;
        } else {
            url = Strings.removeRightOpt(url, "/");
            console.info.println("checking out " + directory);
            session.scm(url).checkout(url, directory, quiet ? console.verbose : console.info);
            stage = SourceStage.forUrl(session, id, directory, url, stageConfiguration);
        }
        return stage;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy