aboutsummaryrefslogtreecommitdiff
path: root/scripts/update_manifest.js
blob: bbd2a5f40bd09b841abe2cc47252d09e735aad42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env node

const fs = require("fs");
const crypto = require("crypto");
const path = require("path");
const ArgumentParser = require("argparse").ArgumentParser;

function isFile(file) {
    if (!fs.existsSync(file)) {
        console.error(`File '${file}' not found. Try again.`);
        throw Error();
    }
    return file;
}

const argParser = new ArgumentParser({
    addHelp: true,
    description: "Adds a new latest version to the manifest file based on the " +
        "provided zip file, including an incremental update."
});
argParser.addArgument("manifestFile", {
    metavar: "<manifest file>", type: isFile
});
argParser.addArgument("version", {
    metavar: "<version>"
});
argParser.addArgument([ "-f", "--full" ], {
    metavar: "<full zip file>", type: isFile, nargs: 1,
    dest: "fullZipFileArgs"
});
argParser.addArgument([ "-i", "--incremental" ], {
    type: isFile, nargs: 2, dest: "incrementalArgs",
    metavar: ["<incremental zip file>", "<file containing list of changed files>"]
});
argParser.addArgument([ "-e", "--executable" ], {
    metavar: "[executable file]", nargs: 1,
    dest: "executableArgs"
});

const {
    manifestFile,
    version,
    fullZipFileArgs,
    incrementalArgs,
    executableArgs
} = argParser.parseArgs();

const [incrementalZipFile, changesFile] = incrementalArgs || [];
const [fullZipFile] = fullZipFileArgs || [];
const [executable] = executableArgs || [];

// Do one final check
if (!incrementalZipFile && !fullZipFile) {
    console.error("No download archive specified! Abort.");
    process.exit(1);
}

// Do a quick litmus test to prevent deleting everything incorrectly
if (changesFile && !fs.existsSync("base")) {
    console.error("The working directory must be set to an " +
                  "asset folder in order for deleted directories " +
                  "to be calculated correctly. Abort.");
    process.exit(1);
}

const manifest = JSON.parse(fs.readFileSync(manifestFile));

const dirsDeleted = new Set();
const specialActions = changesFile ?
    fs.readFileSync(changesFile)
    .toString()
    .trim()
    .split("\n")
    .map(line => line.split("\t"))
    .map(([mode, target, source]) => {
        switch (mode[0]) {
            case "D": // Deleted
                // Check if the folder exists relative to the working
                // directory, and if not, add it to the dirsDeleted list.
                // Keep going up the tree to see how many directories were
                // deleted.
                let dir = path.dirname(target);
                while (!dirsDeleted.has(dir) && !fs.existsSync(dir)) {
                    dirsDeleted.add(dir);
                    dir = path.dirname(dir);
                }

                return { action: "delete", target };
            case "R": // Renamed
                // NOTE: Make sure that the launcher's implementation of
                // the move action also creates directories when needed.
                return { action: "move", source, target};
            default:
                return null;
        }
    })
    // Remove ignored file mode changes
    .filter(action => action !== null)
    // Create actions based on directories to be deleted.
    // Always have deeper directories first, to guarantee that deleting
    // higher-level directories will succeed.
    .concat(Array.from(dirsDeleted.values())
        .sort((a, b) => b.split("/").length - a.split("/").length)
        .map(dir => ({ action: "deleteDir", target: dir })))
    : [];

const urlBase = "https://s3.wasabisys.com/ao-downloads/";

const versionEntry = {
    version,
    executable,
    prev: manifest.versions[0] ? manifest.versions[0].version : undefined,
    full: fullZipFile ? [
        {
            action: "dl",
            url: urlBase + encodeURIComponent(path.basename(fullZipFile)),
            hash: crypto.createHash("sha1")
                .update(fs.readFileSync(fullZipFile))
                .digest("hex")
        }
    ] : undefined,
    update: incrementalArgs ? [
        ...specialActions,
        {
            action: "dl",
            url: urlBase + encodeURIComponent(path.basename(incrementalZipFile)),
            hash: crypto.createHash("sha1")
                .update(fs.readFileSync(incrementalZipFile))
                .digest("hex")
        }
    ] : undefined
};

console.log("Generated version entry:", versionEntry);

const existingVersions = manifest.versions.filter(v => v.version == version);
if (existingVersions.length > 0) {
    console.warn(`Warning: version ${version} already exists. Adding new values.`);

    // Don't overwrite prev - it will cause headaches
    delete versionEntry.prev;

    Object.assign(existingVersions[0], versionEntry);
    console.log("Merged version entry:", existingVersions[0]);
} else {
    manifest.versions = [versionEntry, ...manifest.versions];
}

fs.writeFileSync(manifestFile, JSON.stringify(manifest, null, 4));