some populating...
This commit is contained in:
parent
168c1c3ae8
commit
a90188c16f
141
autorepo.xsd
Normal file
141
autorepo.xsd
Normal file
@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://git.square-r00t.net/OpTools/tree/arch/autorepo/"
|
||||
xmlns="http://git.square-r00t.net/OpTools/tree/arch/autorepo/tree/"
|
||||
xmlns:archrepo="http://git.square-r00t.net/OpTools/tree/arch/autorepo/"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xs:simpleType name="t_posixUserGroup">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:pattern value="[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}$)"/>
|
||||
<xs:pattern value="%same"/>
|
||||
<xs:whiteSpace value="collapse"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="t_posixMode">
|
||||
<xs:restriction base="xs:positiveInteger">
|
||||
<xs:pattern value="[0-7]?[0-7]{3}"/>
|
||||
<xs:whiteSpace value="collapse"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="t_path">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="(/|~/)?([A-Za-z0-9+_.-]+/)*[A-Za-z0-9+_.-]+"/>
|
||||
<xs:whiteSpace value="collapse"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="t_port">
|
||||
<xs:restriction base="xs:positiveInteger">
|
||||
<!-- MAN I wish XSD let you validate based on numerical range. -->
|
||||
<!-- https://stackoverflow.com/a/40213676/733214 -->
|
||||
<xs:pattern value="([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="t_localMirror">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:anyURI">
|
||||
<xs:attribute name="user" type="archrepo:t_posixUserGroup" use="optional" default="%same"/>
|
||||
<xs:attribute name="group" type="archrepo:t_posixUserGroup" use="optional" default="%same"/>
|
||||
<xs:attribute name="fileMode" type="archrepo:t_posixMode" use="optional" default="0600"/>
|
||||
<xs:attribute name="dirMode" type="archrepo:t_posixMode" use="optional" default="0700"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="t_remoteMirror">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:anyURI">
|
||||
<xs:attribute name="user" type="archrepo:t_posixUserGroup"
|
||||
default="%same" use="optional"/>
|
||||
<xs:attribute name="server" type="xs:NMTOKEN" use="required"/>
|
||||
<xs:attribute name="fileMode" type="archrepo:t_posixMode"
|
||||
use="optional" default="0600"/>
|
||||
<xs:attribute name="dirMode" type="archrepo:t_posixMode"
|
||||
use="optional" default="0700"/>
|
||||
<xs:attribute name="port" type="archrepo:t_port"
|
||||
default="22" use="optional"/>
|
||||
<xs:attribute name="key" type="archrepo:t_path"
|
||||
default="~/.ssh/id_rsa" use="optional"/>
|
||||
<xs:attribute name="remoteUser" type="archrepo:t_posixUserGroup"
|
||||
default="%same" use="optional"/>
|
||||
<xs:attribute name="remoteGroup" type="archrepo:t_posixUserGroup"
|
||||
default="%same" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="t_gpgKeyID">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:pattern value="(0[Xx])?[0-9A-Fa-f]{40}"/>
|
||||
<xs:pattern value="(0[Xx])?[0-9A-Fa-f]{8}"/>
|
||||
<xs:pattern value="(0[Xx])?([0-9A-Fa-f]{4} ?){5} *([0-9A-Fa-f]{4}){5}"/>
|
||||
<xs:whiteSpace value="collapse"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:element name="archrepo">
|
||||
<xs:complexType>
|
||||
<xs:choice>
|
||||
<xs:element name="repo" minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:all minOccurs="1">
|
||||
<xs:element name="mirrors" minOccurs="1" maxOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="localMirror"
|
||||
maxOccurs="unbounded"
|
||||
type="archrepo:t_localMirror"/>
|
||||
<xs:element name="remoteMirror"
|
||||
maxOccurs="unbounded"
|
||||
type="archrepo:t_remoteMirror"/>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="packages" minOccurs="1">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="aur"
|
||||
maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="alwaysBuild" default="true"
|
||||
type="xs:boolean" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="pkgbuild"
|
||||
maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:token">
|
||||
<xs:attribute name="path" type="archrepo:t_path"
|
||||
default="." use="optional"/>
|
||||
<xs:attribute name="alwaysBuild" default="true"
|
||||
type="xs:boolean" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:all>
|
||||
<xs:attribute name="name" type="xs:token" use="required"/>
|
||||
<xs:attribute name="staging" type="archrepo:t_path" use="optional" default="."/>
|
||||
<xs:attribute name="signPkgs" type="xs:boolean" use="optional" default="true"/>
|
||||
<xs:attribute name="signDB" type="xs:boolean" use="optional" default="true"/>
|
||||
<xs:attribute name="gnupgHome" type="archrepo:t_path" use="optional" default="~/.gnupg"/>
|
||||
<xs:attribute name="gpgKeyID" type="archrepo:t_gpgKeyID" use="optional"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
191
build.py
Executable file
191
build.py
Executable file
@ -0,0 +1,191 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# TODO: make as flexible as the <rpms>:/bin/build.py (flesh out args), logging, etc.
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import copy
|
||||
import io
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tarfile
|
||||
import tempfile
|
||||
import warnings
|
||||
##
|
||||
import gpg
|
||||
import requests
|
||||
from lxml import etree
|
||||
|
||||
|
||||
# TODO: track which versions are built so we don't need to consistently rebuild ALL packages
|
||||
# TODO: should this be a configuration option?
|
||||
aurbase = 'https://aur.archlinux.org'
|
||||
|
||||
_dflts = {'cfgfile': '~/.config/optools/arch/autorepo.xml'}
|
||||
|
||||
|
||||
class Packager(object):
|
||||
def __init__(self, cfgfile = _dflts['cfgfile'], *args, **kwargs):
|
||||
user_params = kwargs
|
||||
self.args = copy.deepcopy(_dflts)
|
||||
self.args.update(user_params)
|
||||
self.origdir = os.path.abspath(os.path.expanduser(os.getcwd()))
|
||||
self.gpg = None
|
||||
self.args['destdir'] = os.path.abspath(os.path.expanduser(self.args['destdir']))
|
||||
if not self.args['pkgs']:
|
||||
self.args['pkgs'] = _dflts['pkgs']
|
||||
self._initSigner()
|
||||
|
||||
def buildPkgs(self, auronly = None):
|
||||
for p in self.args['pkgs']:
|
||||
print(p)
|
||||
extract_dir = tempfile.mkdtemp(prefix = '.pkgbuilder.{0}-'.format(p))
|
||||
sub_extract_dir = os.path.join(extract_dir, p)
|
||||
has_pkg = False
|
||||
if not auronly:
|
||||
has_pkg = self._getLocal(p, extract_dir)
|
||||
if not has_pkg:
|
||||
has_pkg = self._getAUR(p, extract_dir)
|
||||
if not has_pkg:
|
||||
warnings.warn('Could not find package {0}; skipping...'.format(p))
|
||||
continue
|
||||
# We get a list of files to compare.
|
||||
prebuild_files = []
|
||||
postbuild_files = []
|
||||
for root, dirs, files in os.walk(sub_extract_dir):
|
||||
for f in files:
|
||||
prebuild_files.append(os.path.join(root, f))
|
||||
os.chdir(os.path.join(extract_dir, p))
|
||||
# customizepkg-scripting in AUR
|
||||
try:
|
||||
custpkg_out = subprocess.run(['/usr/bin/customizepkg',
|
||||
'-m'],
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE)
|
||||
except FileNotFoundError:
|
||||
pass # Not installed
|
||||
build_out = subprocess.run(['/usr/bin/multilib-build',
|
||||
'-c',
|
||||
'--',
|
||||
'--',
|
||||
'--skippgpcheck',
|
||||
'--syncdeps',
|
||||
'--noconfirm',
|
||||
'--log',
|
||||
'--holdver',
|
||||
'--skipinteg'],
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE)
|
||||
# with open('/tmp/build.log-{0}'.format(p), 'w') as f:
|
||||
# f.write(build_out.stdout.decode('utf-8'))
|
||||
for root, dirs, files in os.walk(sub_extract_dir):
|
||||
for f in files:
|
||||
fpath = os.path.join(root, f)
|
||||
if fpath in prebuild_files:
|
||||
continue
|
||||
if fpath.endswith('.log'):
|
||||
continue
|
||||
postbuild_files.append(fpath)
|
||||
postbuild_files = [i for i in postbuild_files if i.endswith('.pkg.tar.xz')]
|
||||
if len(postbuild_files) != 1:
|
||||
warnings.warn('Could not reliably find a built package for {0}; skipping'.format(p))
|
||||
else:
|
||||
fdest = os.path.join(self.args['destdir'],
|
||||
os.path.basename(postbuild_files[0]))
|
||||
if os.path.isfile(fdest):
|
||||
os.remove(fdest)
|
||||
shutil.move(postbuild_files[0], fdest)
|
||||
self._sign(fdest)
|
||||
os.chdir(self.origdir)
|
||||
shutil.rmtree(extract_dir)
|
||||
return()
|
||||
|
||||
def _initSigner(self):
|
||||
self.gpg = gpg.Context()
|
||||
# Just grab the first private key until we flesh this out.
|
||||
for k in self.gpg.keylist(secret = True):
|
||||
if k.can_sign:
|
||||
self.gpg.signers = [k]
|
||||
break
|
||||
return()
|
||||
|
||||
def _getAUR(self, pkgnm, extract_dir):
|
||||
dl_url = None
|
||||
pkg_srch = requests.get(os.path.join(self.args['aurbase'],
|
||||
'rpc'),
|
||||
params = {
|
||||
'v': 5,
|
||||
'type': 'search',
|
||||
'by': 'name',
|
||||
'arg': pkgnm}).json()
|
||||
for pkg in pkg_srch['results']:
|
||||
dl_url = None
|
||||
if pkg['Name'] == pkgnm:
|
||||
dl_url = os.path.join(self.args['aurbase'], re.sub('^/+', '', pkg['URLPath']))
|
||||
# dl_file = os.path.basename(pkg['URLPath'])
|
||||
break
|
||||
if not dl_url:
|
||||
warnings.warn('Could not find a download path for {0}; skipping'.format(pkgnm))
|
||||
return(False)
|
||||
with requests.get(dl_url, stream = True) as url:
|
||||
with tarfile.open(mode = 'r|*', fileobj = io.BytesIO(url.content)) as tar:
|
||||
tar.extractall(extract_dir)
|
||||
return(True)
|
||||
|
||||
def _getLocal(self, pkgnm, extract_dir):
|
||||
curfile = os.path.realpath(os.path.abspath(os.path.expanduser(__file__)))
|
||||
localpkg_dir = os.path.abspath(os.path.join(os.path.dirname(curfile),
|
||||
'..',
|
||||
'local_pkgs'))
|
||||
pkgbuild_dir = os.path.join(localpkg_dir,
|
||||
pkgnm)
|
||||
if not os.path.isdir(pkgbuild_dir):
|
||||
return(False)
|
||||
shutil.copytree(pkgbuild_dir, os.path.join(extract_dir, pkgnm))
|
||||
return(True)
|
||||
|
||||
def _sign(self, pkgfile, passphrase = None):
|
||||
sigfile = '{0}.sig'.format(pkgfile)
|
||||
with open(pkgfile, 'rb') as pkg:
|
||||
with open(sigfile, 'wb') as sig:
|
||||
# We want ascii-armoured detached sigs
|
||||
sig.write(self.gpg.sign(pkg.read(), mode = gpg.constants.SIG_MODE_DETACH)[0])
|
||||
return()
|
||||
|
||||
def createRepo(self):
|
||||
pkgfiles = []
|
||||
for root, dirs, files in os.walk(self.args['destdir']):
|
||||
for f in files:
|
||||
if f.endswith('.pkg.tar.xz'):
|
||||
pkgfiles.append(os.path.join(root, f))
|
||||
repo_out = subprocess.run(['/usr/bin/repo-add',
|
||||
'-s',
|
||||
'-R',
|
||||
os.path.join(self.args['destdir'], '{0}.db.tar.xz'.format(self.args['reponame'])),
|
||||
*pkgfiles],
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE)
|
||||
return()
|
||||
|
||||
|
||||
def parseArgs():
|
||||
args = argparse.ArgumentParser(description = 'Build Pacman packages and update a local repository')
|
||||
args.add_argument('-c', '--config',
|
||||
dest = 'cfgfile',
|
||||
default = _dflts['cfgfile'],
|
||||
help = ('The path to the configuration file. Default: {0}').format(_dflts['cfgfile']))
|
||||
return(args)
|
||||
|
||||
def main():
|
||||
args = parseArgs().parse_args()
|
||||
varargs = vars(args)
|
||||
pkgr = Packager(**varargs)
|
||||
pkgr.buildPkgs(auronly = varargs['auronly'])
|
||||
pkgr.createRepo()
|
||||
return()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
112
example.pkgs.xml
Normal file
112
example.pkgs.xml
Normal file
@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--
|
||||
The parsing supports XInclude (https://www.w3.org/TR/xinclude/).
|
||||
You can use external XML snippets if that's easier/cleaner (it usually is).
|
||||
-->
|
||||
<archrepo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://git.square-r00t.net/OpTools/tree/arch/autorepo/"
|
||||
xsi:schemaLocation="http://git.square-r00t.net/OpTools/plain/arch/autorepo/autorepo.xsd">
|
||||
<!--
|
||||
The repo element contains information for each repository we should build for.
|
||||
Attributes:
|
||||
name: The name of the repository. This is used for the db name and to generate pacman.conf snippets.
|
||||
staging: The path to the staging directory. This is where we will build packages and sync to mirrors from.
|
||||
signPkgs: Either "1"/"true" or "0"/"false". Whether or not we should sign packages. See signDB, gnupgHome,
|
||||
and gpgKeyID.
|
||||
signDB: Either "1"/"true" or "0"/"false". Whether or not we should sign the database files. See signPkgs,
|
||||
gnupgHome, and gpgKeyID.
|
||||
gnupgHome: The path to use for the GnuPG home (GNUPGHOME environment variable).
|
||||
The order of preference follows:
|
||||
1.) gnupgHome attribute (if set)
|
||||
2.) $GNUPGHOME env var (if set)
|
||||
3.) ~/.gnupg
|
||||
See signPkgs, signDB, and gpgKeyID.
|
||||
gpgKeyID: The key ID to use. It *must* have the signing ("S") capability. If it is a subkey fingerprint,
|
||||
that subkey will be used. If a subkey fpr is specified but lacks the signing capability, the
|
||||
(parent) key will be used (if it has signing capability). If no key ID/fingerprint/etc. is
|
||||
specified, we will use the first key with signing capability found (this should be fine if you
|
||||
only have one key with signing capabilities in your gnupgHome). If no suitable key is found but
|
||||
signing is enabled, an error will be thrown. See signPkgs, signDB, and gnupgHome.
|
||||
-->
|
||||
<repo
|
||||
name="testrepo"
|
||||
staging="/var/tmp/arch/autorepo"
|
||||
signPkgs="true"
|
||||
signDB="true"
|
||||
gnupgHome="~/.gnupg"
|
||||
gpgKeyID="0x748231EBCBD808A14F5E85D28C004C2F93481F6B">
|
||||
<!--
|
||||
The mirrors element contains either localMirror elements or remoteMirror elements (see below).
|
||||
There must be at least 1 of either type.
|
||||
-->
|
||||
<mirrors>
|
||||
<!-- localMirror elements contain the path to a local mirror (exists on the same system as you're building
|
||||
from). Most users will probably want this if their build box and mirror are the same machine, or if
|
||||
you only want a local repository.
|
||||
Attributes:
|
||||
user: The user to chown the files/directories to (must be running as root user). If not
|
||||
specified, the default is the current user (or the user calling sudo, if done via sudo).
|
||||
group: The group to chown the files/directories to (must be running as root user). If not
|
||||
specified, the default is the primary group for the current user (or the user calling
|
||||
sudo, if done via sudo).
|
||||
fileMode: The octal permissions to chmod the files to.
|
||||
dirMode: The octal permissions to chmod the directories to.
|
||||
-->
|
||||
<localMirror
|
||||
user="foo"
|
||||
group="bar"
|
||||
fileMode="0600"
|
||||
dirMode="0700">/path/to/path</localMirror>
|
||||
<localMirror>a/relative/path</localMirror>
|
||||
<!--
|
||||
The remoteMirror element is for rsyncing packages to a remote mirror/repo server. Rsync must be installed
|
||||
locally (it should; it's part of base-devel) *and* the remote server. Obviously, SSH pubkey auth must also
|
||||
be set up as well for the user. They must have a valid shell on the server for chmodding/chowning.
|
||||
Attributes:
|
||||
user: The (remote) user to sync as (e.g. for "ssh foo@bar", user would be "foo").
|
||||
server: The server to sync to. Can be an IP address, hostname (if resolvable), or FQDN.
|
||||
port: The remote SSH port.
|
||||
key: The pubkey to use to connect.
|
||||
remoteUser: The (remote) user to chown the files/directories to (must be connecting as root user).
|
||||
If not specified, the default is the connecting user ("user" attribute).
|
||||
remoteGroup: The (remote) group to chown the files/directories to (must be connecting as root user).
|
||||
If not specified, the default is the connecting user's ("user" attribute) primary
|
||||
group.
|
||||
fileMode: The octal permissions to chmod the remote files to.
|
||||
dirMode: The octal permissions to chmod the remote directories to.
|
||||
-->
|
||||
<remoteMirror
|
||||
user="foo"
|
||||
server="bar.domain.tld"
|
||||
port="22"
|
||||
key="~/.ssh/id_rsa"
|
||||
remoteUser="foo"
|
||||
remoteGroup="bar"
|
||||
fileMode="0600"
|
||||
dirMode="0700">/path/to/remote/path
|
||||
</remoteMirror>
|
||||
</mirrors>
|
||||
<!--
|
||||
The packages element contains actual packages to build into the repository.
|
||||
-->
|
||||
<packages>
|
||||
<!--
|
||||
The aur element specifies packages that should be fetched and built from the AUR.
|
||||
They contain the name of the package.
|
||||
Attributes:
|
||||
alwaysBuild: Accepts "1"/"true" or "0"/"false". If true, always build the package even if the same
|
||||
version exists already. This only works if you don't delete/empty your staging
|
||||
directory, otherwise it will be built.
|
||||
-->
|
||||
<aur alwaysBuild="true">somepkg</aur>
|
||||
<!--
|
||||
The pkgbuild element specifies packages that are locally developed/designed.
|
||||
They contain the name of the package.
|
||||
Attributes:
|
||||
path: The path to the package to build.
|
||||
-->
|
||||
<pkgbuild path="/path/to/pkgnm.snapshot.tar.gz" alwaysBuild="true">pkgnm</pkgbuild>
|
||||
<pkgbuild path="/path/to/PKGBUILD" alwaysBuild="false">pkgnm2</pkgbuild>
|
||||
</packages>
|
||||
</repo>
|
||||
</archrepo>
|
9
readme.txt
Normal file
9
readme.txt
Normal file
@ -0,0 +1,9 @@
|
||||
This has a lot of work pending. I need to factor in configuration files, etc.
|
||||
|
||||
But it does require the following packages to be installed, and the buildbox (not the repo mirror server itself) needs to be Arch:
|
||||
|
||||
- pacman (duh)
|
||||
- namcap
|
||||
- devtools (for https://wiki.archlinux.org/index.php/DeveloperWiki:Building_in_a_clean_chroot)
|
||||
|
||||
It is designed to be run as a *non-root* user. Use the regen_sudoers.py script to create a sudoers CMND_ALIAS (https://www.sudo.ws/man/1.7.10/sudoers.man.html) to add for your packaging user.
|
33
regen_sudoers.py
Executable file
33
regen_sudoers.py
Executable file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
|
||||
sudo_cmds = []
|
||||
|
||||
# All of these commands...
|
||||
cmds = ['/usr/bin/extra-x86_64-build',
|
||||
'/usr/bin/testing-x86_64-build',
|
||||
'/usr/bin/staging-x86_64-build',
|
||||
'/usr/bin/multilib-build',
|
||||
'/usr/bin/multilib-testing-build',
|
||||
'/usr/bin/multilib-staging-build',
|
||||
'/usr/bin/makechrootpkg']
|
||||
|
||||
# Should allow all of these args.
|
||||
args = ['-c',
|
||||
'-c -- -- --skippgpcheck --syncdeps --noconfirm --log --holdver --skipinteg',
|
||||
'-- -- --skippgpcheck --syncdeps --noconfirm --log --holdver --skipinteg']
|
||||
|
||||
for c in cmds:
|
||||
for a in args:
|
||||
sudo_cmds.append('{0} {1}'.format(c, a))
|
||||
|
||||
s = ''
|
||||
|
||||
s += 'Cmnd_Alias\tPKGBUILDER = \\\n'
|
||||
for c in sudo_cmds:
|
||||
s += '\t\t\t\t{0}, \\\n'.format(c)
|
||||
|
||||
s = re.sub(r', \\s*$', '', s)
|
||||
print(s)
|
||||
|
15
sync.sh
Executable file
15
sync.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This obviously will require some tweaking. Will roll into build.py later.
|
||||
set -e
|
||||
|
||||
server=my_repo.domain.tld
|
||||
port=2222
|
||||
user=pkgrepo
|
||||
src=~/pkgs/built/.
|
||||
# You should use rrsync to restrict to a specific directory
|
||||
dest='Arch/.'
|
||||
|
||||
echo "Syncing..."
|
||||
rsync -a --delete -e "ssh -p ${port}" ${src} ${user}@${server}:${dest}
|
||||
echo "Done."
|
Loading…
Reference in New Issue
Block a user