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

com.st.maven.apt.AptDeployMojo Maven / Gradle / Ivy

package com.st.maven.apt;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.ar.ArArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.gpg.AbstractGpgSigner;
import org.apache.maven.plugin.gpg.GpgMojo;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.wagon.ConnectionException;
import org.apache.maven.wagon.ResourceDoesNotExistException;
import org.apache.maven.wagon.TransferFailedException;
import org.apache.maven.wagon.Wagon;
import org.apache.maven.wagon.authentication.AuthenticationInfo;
import org.apache.maven.wagon.authorization.AuthorizationException;
import org.apache.maven.wagon.repository.Repository;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;

@Mojo(name = "deploy", defaultPhase = LifecyclePhase.DEPLOY, threadSafe = false)
public class AptDeployMojo extends GpgMojo {

	@Parameter(defaultValue = "${maven.deploy.skip}", readonly = true)
	private boolean skip;

	@Parameter(defaultValue = "${project}", readonly = true, required = true)
	private MavenProject project;

	@Component
	private PlexusContainer container;

	@Parameter(defaultValue = "${maven.apt.file}", readonly = true)
	private String file;

	@Parameter(readonly = true, required = true)
	private String codename;

	@Parameter(readonly = true, required = true)
	private String component;

	@Parameter(property = "gpg.sign", readonly = true)
	private boolean sign;

	@Override
	public void execute() throws MojoExecutionException, MojoFailureException {
		if (skip) {
			getLog().info("Skipping artifact deployment");
			return;
		}
		List deb = getDebFiles();
		if (deb.isEmpty()) {
			getLog().info("\"deb\" artifacts not found. skipping");
			return;
		}

		AbstractGpgSigner signer = null;
		if (sign) {
			signer = newSigner(project);
		}

		ArtifactRepository repository = project.getDistributionManagementArtifactRepository();
		if (repository == null) {
			throw new MojoExecutionException("no repository found for distribution");
		}
		Wagon w = null;
		Repository repositoryForWagon = new Repository(repository.getId(), repository.getUrl());
		AuthenticationInfo info = null;
		if (repository.getAuthentication() != null) {
			info = new AuthenticationInfo();
			info.setUserName(repository.getAuthentication().getUsername());
			info.setPassword(repository.getAuthentication().getPassword());
		}
		try {
			w = container.lookup(Wagon.class, repository.getProtocol());
		} catch (ComponentLookupException e) {
			throw new MojoExecutionException("unable to find wagon", e);
		}

		if (w == null) {
			throw new MojoExecutionException("unable to find wagon for: " + repository.getProtocol());
		}

		try {
			w.connect(repositoryForWagon, info);

			Map packagesPerArch = new HashMap<>();

			for (File f : deb) {
				ControlFile controlFile = readControl(f);
				if (controlFile == null) {
					throw new MojoExecutionException("invalid .deb format. Missing control file: " + f.getAbsolutePath());
				}
				String path = "pool/" + component + "/" + controlFile.getPackageName().charAt(0) + "/" + controlFile.getPackageName() + "/" + controlFile.getPackageName() + "_" + controlFile.getVersion() + "_" + controlFile.getArch() + ".deb";
				try {
					FileInfo fileInfo = getFileInfo(f);
					controlFile.append("Filename: " + path);
					controlFile.append("Size: " + fileInfo.getSize());
					controlFile.append("MD5sum: " + fileInfo.getMd5());
					controlFile.append("SHA1: " + fileInfo.getSha1());
					controlFile.append("SHA256: " + fileInfo.getSha256());
				} catch (Exception e) {
					throw new MojoExecutionException("unable to calculate checksum for: " + f.getAbsolutePath(), e);
				}
				if (controlFile.getArch().isWildcard()) {
					for (Architecture cur : Architecture.values()) {
						if (cur.isWildcard()) {
							continue;
						}
						addControlFile(w, cur, controlFile, packagesPerArch);
					}
				} else {
					addControlFile(w, controlFile.getArch(), controlFile, packagesPerArch);
				}
				getLog().info("uploading: " + f.getAbsolutePath());
				w.put(f, path);
			}

			Release release = loadRelease(w);
			// retain old fileinfo
			Map fileinfoByFilename = new HashMap<>();
			for (FileInfo cur : release.getFiles()) {
				fileinfoByFilename.put(cur.getFilename(), cur);
			}
			// add and override with new fileinfo
			for (Packages cur : packagesPerArch.values()) {
				for (FileInfo resultInfo : uploadPackages(w, cur)) {
					fileinfoByFilename.put(resultInfo.getFilename(), resultInfo);
				}
			}
			release.setFiles(new HashSet<>(fileinfoByFilename.values()));

			File releaseFile = File.createTempFile("apt", "releaseFile");
			uploadRelease(w, releaseFile, release);

			if (signer != null) {
				File releaseSignature = signer.generateSignatureForArtifact(releaseFile);
				getLog().info("uploading: Release.gpg");
				w.put(releaseSignature, getReleasePath() + ".gpg");

				signer.setArgs(Collections.singletonList("--clearsign"));
				File clearsigned = signer.generateSignatureForArtifact(releaseFile);
				getLog().info("uploading: InRelease");
				w.put(clearsigned, "dists/" + codename + "/InRelease");
			}

		} catch (Exception e) {
			throw new MojoExecutionException("unable to process", e);
		} finally {
			try {
				w.disconnect();
			} catch (ConnectionException e) {
				getLog().error("unable to disconnect", e);
			}
		}

	}

	private void addControlFile(Wagon w, Architecture arch, ControlFile file, Map packagesPerArch) throws MojoExecutionException {
		Packages curPackages = packagesPerArch.get(arch);
		if (curPackages == null) {
			curPackages = loadPackages(w, arch);
			packagesPerArch.put(arch, curPackages);
		}
		curPackages.add(file);
	}

	private List uploadPackages(Wagon w, Packages packages) throws MojoExecutionException, TransferFailedException, ResourceDoesNotExistException, AuthorizationException {
		OutputStream fos = null;
		List result = new ArrayList<>();
		File tempFile;
		try {
			tempFile = File.createTempFile("apt", packages.getArchitecture().name());
			fos = new FileOutputStream(tempFile);
			packages.save(fos);
		} catch (Exception e) {
			throw new MojoExecutionException("unable to write packages", e);
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					getLog().error("unable to close cursor", e);
				}
			}
		}

		try {
			FileInfo fileInfo = getFileInfo(tempFile);
			fileInfo.setFilename(getPackagesBasePath(packages.getArchitecture()));
			result.add(fileInfo);
		} catch (Exception e) {
			throw new MojoExecutionException("unable to calculate checksum for: " + tempFile.getAbsolutePath(), e);
		}

		String path = getPackagesPath(packages.getArchitecture());
		getLog().info("uploading: " + path);
		w.put(tempFile, path);

		// gzipped
		try {
			tempFile = File.createTempFile("apt", packages.getArchitecture().name());
			fos = new GZIPOutputStream(new FileOutputStream(tempFile));
			packages.save(fos);
		} catch (Exception e) {
			throw new MojoExecutionException("unable to write packages", e);
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					getLog().error("unable to close cursor", e);
				}
			}
		}

		try {
			FileInfo fileInfo = getFileInfo(tempFile);
			fileInfo.setFilename(getPackagesBasePath(packages.getArchitecture()) + ".gz");
			result.add(fileInfo);
		} catch (Exception e) {
			throw new MojoExecutionException("unable to calculate checksum for: " + tempFile.getAbsolutePath(), e);
		}

		getLog().info("uploading: " + path + ".gz");
		w.put(tempFile, path + ".gz");

		return result;
	}

	private Packages loadPackages(Wagon w, Architecture architecture) throws MojoExecutionException {
		Packages packages = new Packages();
		packages.setArchitecture(architecture);
		InputStream fis = null;
		try {
			File tempFile = File.createTempFile("packages", architecture.name());
			w.get(getPackagesPath(architecture) + ".gz", tempFile);
			fis = new GZIPInputStream(new FileInputStream(tempFile));
			packages.load(fis);
		} catch (ResourceDoesNotExistException e) {
			getLog().info(packages.getArchitecture() + "/Packages.gz do not exist. creating...");
		} catch (Exception e) {
			throw new MojoExecutionException("unable to load " + getPackagesPath(architecture), e);
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					getLog().error("unable to close cursor", e);
				}
			}
		}
		return packages;
	}

	private void uploadRelease(Wagon w, File releaseFile, Release release) throws MojoExecutionException, TransferFailedException, ResourceDoesNotExistException, AuthorizationException {
		OutputStream fos = null;
		try {
			fos = new FileOutputStream(releaseFile);
			release.save(fos);
		} catch (Exception e) {
			throw new MojoExecutionException("unable to write releases", e);
		} finally {
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					getLog().error("unable to close cursor", e);
				}
			}
		}

		getLog().info("uploading: Release");
		w.put(releaseFile, getReleasePath());
	}

	private Release loadRelease(Wagon w) throws MojoExecutionException {
		InputStream fis = null;
		Release release = new Release();
		try {
			File tempFile = File.createTempFile("release", "file");
			w.get(getReleasePath(), tempFile);
			fis = new FileInputStream(tempFile);
			release.load(fis);
		} catch (ResourceDoesNotExistException e) {
			getLog().info("Release do not exist. creating...");
			release.setArchitectures("amd64");
			release.setCodename(codename);
			release.setComponents(component);
			release.setLabel(codename);
			release.setOrigin(codename);
		} catch (Exception e) {
			throw new MojoExecutionException("unable to read Release from: " + getReleasePath(), e);
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					getLog().error("unable to close cursor", e);
				}
			}
		}
		SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
		sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
		release.setDate(sdf.format(new Date()));
		return release;
	}

	private String getPackagesBasePath(Architecture architecture) {
		String packagesBaseFilename = component + "/binary-" + architecture.name() + "/Packages";
		return packagesBaseFilename;
	}

	private String getPackagesPath(Architecture architecture) {
		String packagesFilename = "dists/" + codename + "/" + getPackagesBasePath(architecture);
		return packagesFilename;
	}

	private String getReleasePath() {
		String releaseFilename = "dists/" + codename + "/Release";
		return releaseFilename;
	}

	private List getDebFiles() throws MojoExecutionException {
		List attachedArtefacts = project.getAttachedArtifacts();
		List deb = new ArrayList();
		for (Artifact cur : attachedArtefacts) {
			if (cur.getType().equals("deb")) {
				deb.add(cur.getFile());
			}
		}
		if (file != null && file.trim().length() != 0) {
			File f = new File(file);
			if (!f.exists()) {
				throw new MojoExecutionException("specified file not found: " + f.getAbsolutePath());
			}
			deb.add(f);
		}
		return deb;
	}

	private static FileInfo getFileInfo(File f) throws Exception {
		FileInfo result = new FileInfo();
		result.setSize(String.valueOf(f.length()));
		BufferedInputStream bis = null;
		try {
			bis = new BufferedInputStream(new FileInputStream(f));
			MessageDigest md5Alg = MessageDigest.getInstance("MD5");
			md5Alg.reset();
			MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
			sha1.reset();
			MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
			sha256.reset();
			byte[] buf = new byte[2048];
			int curByte = -1;
			while ((curByte = bis.read(buf)) != -1) {
				md5Alg.update(buf, 0, curByte);
				sha1.update(buf, 0, curByte);
				sha256.update(buf, 0, curByte);
			}
			result.setMd5(new String(Hex.encodeHex(md5Alg.digest())));
			result.setSha1(new String(Hex.encodeHex(sha1.digest())));
			result.setSha256(new String(Hex.encodeHex(sha256.digest())));
		} finally {
			if (bis != null) {
				bis.close();
			}
		}
		return result;
	}

	private ControlFile readControl(File deb) throws MojoExecutionException {
		ArArchiveEntry entry;
		TarArchiveEntry controlEntry;
		ArchiveInputStream debStream = null;
		try {
			debStream = new ArchiveStreamFactory().createArchiveInputStream("ar", new FileInputStream(deb));
			while ((entry = (ArArchiveEntry) debStream.getNextEntry()) != null) {
				if (entry.getName().equals("control.tar.gz")) {
					try (ArchiveInputStream controlTgz = new ArchiveStreamFactory().createArchiveInputStream("tar", new GZIPInputStream(debStream))) {
						while ((controlEntry = (TarArchiveEntry) controlTgz.getNextEntry()) != null) {
							getLog().debug("control entry: " + controlEntry.getName());
							if (controlEntry.getName().equals("./control") || controlEntry.getName().equals("control")) {
								ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
								IOUtils.copy(controlTgz, outputStream);
								String contentString = outputStream.toString("UTF-8");
								outputStream.close();
								ControlFile controlFile = new ControlFile();
								controlFile.load(contentString);
								return controlFile;
							}
						}
					}
				}
			}
			return null;
		} catch (Exception e) {
			throw new MojoExecutionException("invalid .deb. unable to find control at: " + deb.getAbsolutePath(), e);
		} finally {
			if (debStream != null) {
				try {
					debStream.close();
				} catch (IOException e) {
					getLog().error("unable to close .deb", e);
				}
			}
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy