forked from Qortal/qortal
Merge branch 'master' into sync-multiple-blocks
# Conflicts: # src/main/java/org/qortal/settings/Settings.java
This commit is contained in:
commit
a48a9592d0
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,7 +21,7 @@
|
||||
/qortal.iml
|
||||
.DS_Store
|
||||
/src/main/resources/resources
|
||||
/src/main/resources/log*.properties
|
||||
/*.jar
|
||||
/run.pid
|
||||
/run.log
|
||||
/WindowsInstaller/Install Files/qortal.jar
|
||||
|
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<DOCUMENT Type="Advanced Installer" CreateVersion="14.9" version="18.2" Modules="enterprise" RootPath="." Language="en_GB" Id="{713E21E0-28FC-422F-8A95-823D01A5F80B}">
|
||||
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
|
||||
<ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
|
||||
@ -17,10 +17,10 @@
|
||||
<ROW Property="Manufacturer" Value="Qortal"/>
|
||||
<ROW Property="MsiLogging" MultiBuildValue="DefaultBuild:vp"/>
|
||||
<ROW Property="NTP_GOOD" Value="false"/>
|
||||
<ROW Property="ProductCode" Value="1033:{259B155C-9CE9-4EF7-AA89-CFF2F118196B} 1049:{61A8713C-B9C5-489D-92FF-0A578697E1C2} 2052:{C59AF8F7-326D-4C43-A02D-7F4BAB56861A} 2057:{58DEB696-96AD-49DC-AD18-02B7B04FDAE3} " Type="16"/>
|
||||
<ROW Property="ProductCode" Value="1033:{EB5562C3-664E-4A8B-8907-6D2033B98836} 1049:{36D0E774-B970-4A13-BCC4-1BA6AB3B2633} 2052:{AF6B6B44-9404-403A-B00F-B7110C28E453} 2057:{68BB9EB8-5991-42E5-841C-E76ACE51166D} " Type="16"/>
|
||||
<ROW Property="ProductLanguage" Value="2057"/>
|
||||
<ROW Property="ProductName" Value="Qortal"/>
|
||||
<ROW Property="ProductVersion" Value="1.5.3" Type="32"/>
|
||||
<ROW Property="ProductVersion" Value="1.5.4" Type="32"/>
|
||||
<ROW Property="RECONFIG_NTP" Value="true"/>
|
||||
<ROW Property="REMOVE_BLOCKCHAIN" Value="YES" Type="4"/>
|
||||
<ROW Property="REPAIR_BLOCKCHAIN" Value="YES" Type="4"/>
|
||||
@ -212,7 +212,7 @@
|
||||
<ROW Component="ADDITIONAL_LICENSE_INFO_71" ComponentId="{12A3ADBE-BB7A-496C-8869-410681E6232F}" Directory_="jdk.zipfs_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_71" Type="0"/>
|
||||
<ROW Component="ADDITIONAL_LICENSE_INFO_8" ComponentId="{D53AD95E-CF96-4999-80FC-5812277A7456}" Directory_="java.naming_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_8" Type="0"/>
|
||||
<ROW Component="ADDITIONAL_LICENSE_INFO_9" ComponentId="{6B7EA9B0-5D17-47A8-B78C-FACE86D15E01}" Directory_="java.net.http_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_9" Type="0"/>
|
||||
<ROW Component="AI_CustomARPName" ComponentId="{5B3B8F5D-88BD-495F-9DD4-F749533C1F89}" Directory_="APPDIR" Attributes="260" KeyPath="DisplayName" Options="1"/>
|
||||
<ROW Component="AI_CustomARPName" ComponentId="{83DFE721-3F68-4ABE-8697-8EC3A91EEB8A}" Directory_="APPDIR" Attributes="260" KeyPath="DisplayName" Options="1"/>
|
||||
<ROW Component="AI_ExePath" ComponentId="{3644948D-AE0B-41BB-9FAF-A79E70490A08}" Directory_="APPDIR" Attributes="260" KeyPath="AI_ExePath"/>
|
||||
<ROW Component="APPDIR" ComponentId="{680DFDDE-3FB4-47A5-8FF5-934F576C6F91}" Directory_="APPDIR" Attributes="0"/>
|
||||
<ROW Component="AccessBridgeCallbacks.h" ComponentId="{288055D1-1062-47A3-AA44-5601B4E38AED}" Directory_="bridge_Dir" Attributes="0" KeyPath="AccessBridgeCallbacks.h" Type="0"/>
|
||||
|
@ -12,7 +12,7 @@ configured paths, or create a dummy `D:` drive with the expected layout.
|
||||
|
||||
Typical build procedure:
|
||||
|
||||
* Overwrite the `qortal.jar` file in `Install-Files\`
|
||||
* Place the `qortal.jar` file in `Install-Files\`
|
||||
* Open AdvancedInstaller with qortal.aip file
|
||||
* If releasing a new version, change version number in:
|
||||
+ "Product Information" side menu
|
||||
|
2
pom.xml
2
pom.xml
@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.qortal</groupId>
|
||||
<artifactId>qortal</artifactId>
|
||||
<version>1.5.3</version>
|
||||
<version>1.5.4</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<skipTests>true</skipTests>
|
||||
|
@ -738,6 +738,7 @@ public class Controller extends Thread {
|
||||
hasStatusChanged = true;
|
||||
}
|
||||
}
|
||||
peer.setSyncInProgress(true);
|
||||
|
||||
if (hasStatusChanged)
|
||||
updateSysTray();
|
||||
@ -817,6 +818,7 @@ public class Controller extends Thread {
|
||||
return syncResult;
|
||||
} finally {
|
||||
isSynchronizing = false;
|
||||
peer.setSyncInProgress(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,9 @@ public class Synchronizer {
|
||||
|
||||
|
||||
|
||||
// Keep track of the size of the last re-org, so it can be logged
|
||||
private int lastReorgSize;
|
||||
|
||||
private static Synchronizer instance;
|
||||
|
||||
public enum SynchronizationResult {
|
||||
@ -515,9 +518,22 @@ public class Synchronizer {
|
||||
byte[] peersLastBlockSignature = peerChainTipData.getLastBlockSignature();
|
||||
|
||||
byte[] ourLastBlockSignature = ourLatestBlockData.getSignature();
|
||||
LOGGER.debug(String.format("Synchronizing with peer %s at height %d, sig %.8s, ts %d; our height %d, sig %.8s, ts %d", peer,
|
||||
String syncString = String.format("Synchronizing with peer %s at height %d, sig %.8s, ts %d; our height %d, sig %.8s, ts %d", peer,
|
||||
peerHeight, Base58.encode(peersLastBlockSignature), peer.getChainTipData().getLastBlockTimestamp(),
|
||||
ourInitialHeight, Base58.encode(ourLastBlockSignature), ourLatestBlockData.getTimestamp()));
|
||||
ourInitialHeight, Base58.encode(ourLastBlockSignature), ourLatestBlockData.getTimestamp());
|
||||
|
||||
// If our latest block is very old, we should log that we're attempting to sync with a peer
|
||||
// Otherwise, it can appear as though nothing is happening for a while after launch
|
||||
final Long minLatestBlockTimestamp = Controller.getMinimumLatestBlockTimestamp();
|
||||
if (minLatestBlockTimestamp != null && ourLatestBlockData.getTimestamp() < minLatestBlockTimestamp) {
|
||||
LOGGER.info(syncString);
|
||||
}
|
||||
else {
|
||||
LOGGER.debug(syncString);
|
||||
}
|
||||
|
||||
// Reset last re-org size as we are starting a new sync round
|
||||
this.lastReorgSize = 0;
|
||||
|
||||
List<BlockSummaryData> peerBlockSummaries = new ArrayList<>();
|
||||
SynchronizationResult findCommonBlockResult = fetchSummariesFromCommonBlock(repository, peer, ourInitialHeight, force, peerBlockSummaries, true);
|
||||
@ -576,10 +592,19 @@ public class Synchronizer {
|
||||
// Commit
|
||||
repository.saveChanges();
|
||||
|
||||
// Create string for logging
|
||||
final BlockData newLatestBlockData = repository.getBlockRepository().getLastBlock();
|
||||
LOGGER.info(String.format("Synchronized with peer %s to height %d, sig %.8s, ts: %d", peer,
|
||||
String syncLog = String.format("Synchronized with peer %s to height %d, sig %.8s, ts: %d", peer,
|
||||
newLatestBlockData.getHeight(), Base58.encode(newLatestBlockData.getSignature()),
|
||||
newLatestBlockData.getTimestamp()));
|
||||
newLatestBlockData.getTimestamp());
|
||||
|
||||
// Append re-org info
|
||||
if (this.lastReorgSize > 0) {
|
||||
syncLog = syncLog.concat(String.format(", size: %d", this.lastReorgSize));
|
||||
}
|
||||
|
||||
// Log sync info
|
||||
LOGGER.info(syncLog);
|
||||
|
||||
return SynchronizationResult.OK;
|
||||
} finally {
|
||||
@ -933,6 +958,7 @@ public class Synchronizer {
|
||||
// Unwind to common block (unless common block is our latest block)
|
||||
int ourHeight = ourInitialHeight;
|
||||
LOGGER.debug(String.format("Orphaning blocks back to common block height %d, sig %.8s. Our height: %d", commonBlockHeight, commonBlockSig58, ourHeight));
|
||||
int reorgSize = ourHeight - commonBlockHeight;
|
||||
|
||||
BlockData orphanBlockData = repository.getBlockRepository().fromHeight(ourInitialHeight);
|
||||
while (ourHeight > commonBlockHeight) {
|
||||
@ -981,6 +1007,7 @@ public class Synchronizer {
|
||||
Controller.getInstance().onNewBlock(newBlock.getBlockData());
|
||||
}
|
||||
|
||||
this.lastReorgSize = reorgSize;
|
||||
return SynchronizationResult.OK;
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,11 @@ public class Bitcoin extends Bitcoiny {
|
||||
new Server("192.166.219.200", Server.ConnectionType.SSL, 50002),
|
||||
new Server("2ex.digitaleveryware.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("dxm.no-ip.biz", Server.ConnectionType.SSL, 50002),
|
||||
new Server("caleb.vegas", Server.ConnectionType.SSL, 50002));
|
||||
new Server("caleb.vegas", Server.ConnectionType.SSL, 50002),
|
||||
new Server("ecdsa.net", Server.ConnectionType.SSL, 110),
|
||||
new Server("electrum.hsmiths.com", Server.ConnectionType.SSL, 995),
|
||||
new Server("elec.luggs.co", Server.ConnectionType.SSL, 443),
|
||||
new Server("btc.smsys.me", Server.ConnectionType.SSL, 995));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,7 +51,10 @@ public class Litecoin extends Bitcoiny {
|
||||
new Server("ltc.rentonisk.com", Server.ConnectionType.TCP, 50001),
|
||||
new Server("ltc.rentonisk.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum-ltc.petrkr.net", Server.ConnectionType.SSL, 60002),
|
||||
new Server("ltc.litepay.ch", Server.ConnectionType.SSL, 50022));
|
||||
new Server("ltc.litepay.ch", Server.ConnectionType.SSL, 50022),
|
||||
new Server("electrum-ltc-bysh.me", Server.ConnectionType.TCP, 50002),
|
||||
new Server("electrum.jochen-hoenicke.de", Server.ConnectionType.TCP, 50005),
|
||||
new Server("node.ispol.sk", Server.ConnectionType.TCP, 50004));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -72,7 +72,8 @@ public class Network {
|
||||
private static final String[] INITIAL_PEERS = new String[]{
|
||||
"node1.qortal.org", "node2.qortal.org", "node3.qortal.org", "node4.qortal.org", "node5.qortal.org",
|
||||
"node6.qortal.org", "node7.qortal.org", "node8.qortal.org", "node9.qortal.org", "node10.qortal.org",
|
||||
"node.qortal.ru", "node2.qortal.ru", "node3.qortal.ru", "node.qortal.uk"
|
||||
"node.qortal.ru", "node2.qortal.ru", "node3.qortal.ru", "node.qortal.uk", "node22.qortal.org",
|
||||
"cinfu1.crowetic.com", "node.cwd.systems"
|
||||
};
|
||||
|
||||
private static final long NETWORK_EPC_KEEPALIVE = 10L; // seconds
|
||||
@ -80,6 +81,8 @@ public class Network {
|
||||
public static final int MAX_SIGNATURES_PER_REPLY = 500;
|
||||
public static final int MAX_BLOCK_SUMMARIES_PER_REPLY = 500;
|
||||
|
||||
private static final long DISCONNECTION_CHECK_INTERVAL = 10 * 1000L; // milliseconds
|
||||
|
||||
// Generate our node keys / ID
|
||||
private final Ed25519PrivateKeyParameters edPrivateKeyParams = new Ed25519PrivateKeyParameters(new SecureRandom());
|
||||
private final Ed25519PublicKeyParameters edPublicKeyParams = edPrivateKeyParams.generatePublicKey();
|
||||
@ -89,6 +92,8 @@ public class Network {
|
||||
private final int minOutboundPeers;
|
||||
private final int maxPeers;
|
||||
|
||||
private long nextDisconnectionCheck = 0L;
|
||||
|
||||
private final List<PeerData> allKnownPeers = new ArrayList<>();
|
||||
private final List<Peer> connectedPeers = new ArrayList<>();
|
||||
private final List<PeerAddress> selfPeers = new ArrayList<>();
|
||||
@ -611,6 +616,8 @@ public class Network {
|
||||
// Don't consider already connected peers (resolved address match)
|
||||
// XXX This might be too slow if we end up waiting a long time for hostnames to resolve via DNS
|
||||
peers.removeIf(isResolvedAsConnectedPeer);
|
||||
|
||||
this.checkLongestConnection(now);
|
||||
}
|
||||
|
||||
// Any left?
|
||||
@ -668,6 +675,29 @@ public class Network {
|
||||
return null;
|
||||
}
|
||||
|
||||
private void checkLongestConnection(Long now) {
|
||||
if (now == null || now < nextDisconnectionCheck) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find peers that have reached their maximum connection age, and disconnect them
|
||||
List<Peer> peersToDisconnect = this.connectedPeers.stream()
|
||||
.filter(peer -> !peer.isSyncInProgress())
|
||||
.filter(peer -> peer.hasReachedMaxConnectionAge())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (peersToDisconnect != null && peersToDisconnect.size() > 0) {
|
||||
for (Peer peer : peersToDisconnect) {
|
||||
LOGGER.info("Forcing disconnection of peer {} because connection age ({} ms) " +
|
||||
"has reached the maximum ({} ms)", peer, peer.getConnectionAge(), peer.getMaxConnectionAge());
|
||||
peer.disconnect("Connection age too old");
|
||||
}
|
||||
}
|
||||
|
||||
// Check again after a minimum fixed interval
|
||||
nextDisconnectionCheck = now + DISCONNECTION_CHECK_INTERVAL;
|
||||
}
|
||||
|
||||
// Peer callbacks
|
||||
|
||||
protected void wakeupChannelSelector() {
|
||||
|
@ -84,6 +84,7 @@ public class Peer {
|
||||
private Handshake handshakeStatus = Handshake.STARTED;
|
||||
private volatile boolean handshakeMessagePending = false;
|
||||
private long handshakeComplete = -1L;
|
||||
private long maxConnectionAge = 0L;
|
||||
|
||||
/**
|
||||
* Timestamp of when socket was accepted, or connected.
|
||||
@ -101,6 +102,8 @@ public class Peer {
|
||||
|
||||
byte[] ourChallenge;
|
||||
|
||||
private boolean syncInProgress = false;
|
||||
|
||||
// Versioning
|
||||
public static final Pattern VERSION_PATTERN = Pattern.compile(Controller.VERSION_PREFIX
|
||||
+ "(\\d{1,3})\\.(\\d{1,5})\\.(\\d{1,5})");
|
||||
@ -197,10 +200,24 @@ public class Peer {
|
||||
this.handshakeStatus = handshakeStatus;
|
||||
if (handshakeStatus.equals(Handshake.COMPLETED)) {
|
||||
this.handshakeComplete = System.currentTimeMillis();
|
||||
this.generateRandomMaxConnectionAge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateRandomMaxConnectionAge() {
|
||||
// Retrieve the min and max connection time from the settings, and calculate the range
|
||||
final int minPeerConnectionTime = Settings.getInstance().getMinPeerConnectionTime();
|
||||
final int maxPeerConnectionTime = Settings.getInstance().getMaxPeerConnectionTime();
|
||||
final int peerConnectionTimeRange = maxPeerConnectionTime - minPeerConnectionTime;
|
||||
|
||||
// Generate a random number between the min and the max
|
||||
Random random = new Random();
|
||||
this.maxConnectionAge = (random.nextInt(peerConnectionTimeRange) + minPeerConnectionTime) * 1000L;
|
||||
LOGGER.debug(String.format("Generated max connection age for peer %s. Min: %ds, max: %ds, range: %ds, random max: %dms", this, minPeerConnectionTime, maxPeerConnectionTime, peerConnectionTimeRange, this.maxConnectionAge));
|
||||
|
||||
}
|
||||
|
||||
protected void resetHandshakeMessagePending() {
|
||||
this.handshakeMessagePending = false;
|
||||
}
|
||||
@ -330,6 +347,14 @@ public class Peer {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSyncInProgress() {
|
||||
return this.syncInProgress;
|
||||
}
|
||||
|
||||
public void setSyncInProgress(boolean syncInProgress) {
|
||||
this.syncInProgress = syncInProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Easier, and nicer output, than peer.getRemoteSocketAddress()
|
||||
@ -810,4 +835,12 @@ public class Peer {
|
||||
}
|
||||
return handshakeComplete;
|
||||
}
|
||||
|
||||
public long getMaxConnectionAge() {
|
||||
return maxConnectionAge;
|
||||
}
|
||||
|
||||
public boolean hasReachedMaxConnectionAge() {
|
||||
return this.getConnectionAge() > this.getMaxConnectionAge();
|
||||
}
|
||||
}
|
||||
|
@ -133,6 +133,11 @@ public class Settings {
|
||||
* If false, sync will be blocked both ways, and they will not appear in the peers list */
|
||||
private boolean allowConnectionsWithOlderPeerVersions = true;
|
||||
|
||||
/** Minimum time (in seconds) that we should attempt to remain connected to a peer for */
|
||||
private int minPeerConnectionTime = 2 * 60; // seconds
|
||||
/** Maximum time (in seconds) that we should attempt to remain connected to a peer for */
|
||||
private int maxPeerConnectionTime = 20 * 60; // seconds
|
||||
|
||||
/** Whether to sync multiple blocks at once in normal operation */
|
||||
private boolean fastSyncEnabled = true;
|
||||
/** Whether to sync multiple blocks at once when the peer has a different chain */
|
||||
@ -436,6 +441,10 @@ public class Settings {
|
||||
|
||||
public boolean getAllowConnectionsWithOlderPeerVersions() { return this.allowConnectionsWithOlderPeerVersions; }
|
||||
|
||||
public int getMinPeerConnectionTime() { return this.minPeerConnectionTime; }
|
||||
|
||||
public int getMaxPeerConnectionTime() { return this.maxPeerConnectionTime; }
|
||||
|
||||
public String getBlockchainConfig() {
|
||||
return this.blockchainConfig;
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Change this to where AdvancedInstaller outputs built EXE installers
|
||||
WINDOWS_INSTALLER_DIR=/home/transfer/Qortal/Qortal-SetupFiles
|
||||
SCRIPT_DIR=$(dirname $(realpath "$0"))
|
||||
WINDOWS_INSTALLER_DIR="${SCRIPT_DIR}/../WindowsInstaller/Qortal-SetupFiles"
|
||||
|
||||
set -e
|
||||
shopt -s expand_aliases
|
||||
@ -16,6 +17,16 @@ saved_pwd=$PWD
|
||||
|
||||
alias SHA256='(sha256 -q || sha256sum | cut -d" " -f1) 2>/dev/null'
|
||||
|
||||
function 3hash {
|
||||
local zip_src=$1
|
||||
local md5hash=$(md5 ${zip_src} | awk '{ print $NF }')
|
||||
local sha1hash=$(shasum ${zip_src} | awk '{ print $1 }')
|
||||
local sha256hash=$(sha256sum ${zip_src} | awk '{ print $1 }')
|
||||
echo "\`MD5: ${md5hash}\`"
|
||||
echo "\`SHA1: ${sha1hash}\`"
|
||||
echo "\`SHA256: ${sha256hash}\`"
|
||||
}
|
||||
|
||||
# Check we are within a git repo
|
||||
git_dir=$( git rev-parse --show-toplevel )
|
||||
if [ -z "${git_dir}" ]; then
|
||||
@ -27,7 +38,7 @@ fi
|
||||
cd ${git_dir}
|
||||
|
||||
# Check we are in 'master' branch
|
||||
# branch_name=$( git symbolic-ref -q HEAD )
|
||||
# branch_name=$( git symbolic-ref -q HEAD ) || echo "Cannot determine branch name" && exit 1
|
||||
# branch_name=${branch_name##refs/heads/}
|
||||
# if [ "${branch_name}" != "master" ]; then
|
||||
# echo "Unexpected current branch '${branch_name}' - expecting 'master'"
|
||||
@ -56,7 +67,7 @@ git_url=https://github.com/${git_url##*:}
|
||||
git_url=${git_url%%.git}
|
||||
|
||||
# Check for EXE
|
||||
exe=${project^}-${git_tag#v}.exe
|
||||
exe=${project}-${git_tag#v}.exe
|
||||
exe_src="${WINDOWS_INSTALLER_DIR}/${exe}"
|
||||
if [ ! -r "${exe_src}" ]; then
|
||||
echo "Cannot find EXE installer at ${exe_src}"
|
||||
|
@ -21,13 +21,13 @@ fi
|
||||
cd ${git_dir}
|
||||
|
||||
# Check we are in 'master' branch
|
||||
branch_name=$( git symbolic-ref -q HEAD )
|
||||
branch_name=${branch_name##refs/heads/}
|
||||
echo "Current git branch: ${branch_name}"
|
||||
if [ "${branch_name}" != "master" ]; then
|
||||
echo "Unexpected current branch '${branch_name}' - expecting 'master'"
|
||||
exit 1
|
||||
fi
|
||||
# branch_name=$( git symbolic-ref -q HEAD ) || echo "Cannot determine branch name" && exit 1
|
||||
# branch_name=${branch_name##refs/heads/}
|
||||
# echo "Current git branch: ${branch_name}"
|
||||
# if [ "${branch_name}" != "master" ]; then
|
||||
# echo "Unexpected current branch '${branch_name}' - expecting 'master'"
|
||||
# exit 1
|
||||
# fi
|
||||
|
||||
# Determine project name
|
||||
project=$( perl -n -e 'if (m/<artifactId>(\w+)<.artifactId>/) { print $1; exit }' pom.xml $)
|
||||
@ -60,7 +60,7 @@ git show HEAD:stop.sh > ${build_dir}/stop.sh
|
||||
|
||||
printf "{\n}\n" > ${build_dir}/settings.json
|
||||
|
||||
touch -d ${commit_ts%%+??:??} ${build_dir} ${build_dir}/*
|
||||
gtouch -d ${commit_ts%%+??:??} ${build_dir} ${build_dir}/*
|
||||
|
||||
rm -f ${saved_pwd}/${project}.zip
|
||||
(cd ${build_dir}/..; 7z a -r -tzip ${saved_pwd}/${project}-${git_tag#v}.zip ${project}/)
|
||||
|
Loading…
Reference in New Issue
Block a user