
net.oneandone.stool.Create Maven / Gradle / Ivy
/**
* 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;
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.cli.ArgumentException;
import net.oneandone.sushi.cli.Option;
import net.oneandone.sushi.cli.Remaining;
import net.oneandone.sushi.cli.Value;
import net.oneandone.sushi.fs.World;
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.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class Create extends SessionCommand {
@Option("quiet")
private boolean quiet = false;
@Option("name")
private String name = null;
@Value(name = "url", position = 1)
private String urlOrFileOrSearch;
private FileNode directory;
private final Map config = new LinkedHashMap<>();
private StageConfiguration stageConfiguration;
private final Map properties;
public Create(Session session) {
super(session, Mode.NONE);
this.properties = StageConfiguration.properties(session.extensionsFactory);
}
public Create(Session session, boolean quiet, String name, String urlOrSearch, FileNode directory,
StageConfiguration stageConfiguration) {
this(session);
this.quiet = quiet;
this.name = name;
this.urlOrFileOrSearch = urlOrSearch;
this.directory = directory;
this.stageConfiguration = stageConfiguration;
}
@Remaining
public void remaining(String str) {
int idx;
String key;
String value;
Property property;
if (directory == null) {
this.directory = world.file(str);
} else {
idx = str.indexOf('=');
if (idx == -1) {
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 doInvoke() throws Exception {
FileNode backstage;
Stage stage;
String url;
RmRfThread cleanup;
url = url();
defaults(url);
backstage = session.backstages.join(name);
cleanup = new RmRfThread(console);
cleanup.add(directory);
cleanup.add(backstage);
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(backstage, url);
Runtime.getRuntime().removeShutdownHook(cleanup);
console.info.println("stage created: " + name);
if (session.getSelectedStageName() == null) {
session.backupEnvironment();
}
session.select(stage);
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 pommes ...");
substring = urlOrFileOrSearch.substring(1);
urls = pommes(console.world, substring);
if (urls.size() == 0) {
throw new ArgumentException("not found: " + substring);
}
for (int i = urls.size(); i > 0; --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 urls.get(no - 1);
} catch (NumberFormatException e) {
console.info.println("invalid input: " + e.getMessage());
}
}
}
private void defaults(String url) throws IOException {
FileNode parent;
if (directory == null) {
directory = (FileNode) console.world.getWorking().join(Stage.nameForUrl(url));
}
parent = directory.getParent();
if (session.stageDirectories().contains(parent)) {
directory = parent.getParent().join(directory.getName());
console.verbose.println("warning: cannot create a stage within a stage. Changing directory to " + directory.getAbsolute());
}
if (directory.hasDifferentAnchestor(session.backstages)) {
throw new ArgumentException("you cannot create a stage in the backstages directory");
}
if (directory.isDirectory()) {
throw new ArgumentException("stage directory already exists: " + directory);
}
if (!parent.isDirectory()) {
throw new ArgumentException("parent directory for new stage does not exist: " + directory.getParent());
}
if (session.configuration.shared) {
checkPermissions(directory.getParent());
}
session.checkDiskFree();
if (name == null) {
name = directory.getName();
}
Stage.checkName(name);
if (session.backstages.join(name).exists()) {
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(FileNode backstage, String url) throws Exception {
Stage stage;
Files.createSourceDirectory(console.verbose, directory, session.group());
// CAUTION: create backstage before running possible prepare commands -- e.g. pws already populates the local repository of the stage
Files.createStoolDirectory(console.verbose, backstage);
stage = stage(backstage, url);
stage.tuneConfiguration();
for (Map.Entry entry : config.entrySet()) {
entry.getKey().set(stage.config(), entry.getValue());
}
stage.initialize();
return stage;
}
private Stage stage(FileNode backstage, String url) throws Exception {
ArtifactStage artifactStage;
Stage stage;
String prepare;
if (ArtifactStage.isArtifact(url)) {
artifactStage = new ArtifactStage(session, url, backstage, directory, stageConfiguration);
artifactStage.populateDirectory(console);
stage = artifactStage;
} else {
url = Strings.removeRightOpt(url, "/");
console.info.println("checking out " + directory);
session.subversion().checkout(directory.getParent(), url, directory.getName(), quiet ? console.verbose : console.info);
stage = SourceStage.forUrl(session, backstage, directory, url, stageConfiguration);
// 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);
}
}
}
return stage;
}
public static List pommes(World world, String search) throws IOException {
FileNode working;
List result;
working = (FileNode) world.getWorking();
result = new ArrayList<>();
for (String line : Separator.RAW_LINE.split(working.exec("bash", "--login", "-c", "pommes find -format %o " + search))) {
line = line.trim();
line = Strings.removeRightOpt(line.trim(), "/pom.xml");
line = Strings.removeLeftOpt(line, "svn:");
result.add(line);
}
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy