-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathcreateGithubRelease.ts
More file actions
173 lines (147 loc) · 5.5 KB
/
createGithubRelease.ts
File metadata and controls
173 lines (147 loc) · 5.5 KB
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import fs from "fs";
import path from "path";
import Listr from "listr";
import { getRepoSlugFromManifest } from "../utils/getRepoSlugFromManifest";
import { getPublishTxLink, getInstallDnpLink } from "../utils/getLinks";
import { getGitHead } from "../utils/git";
import { contentHashFile, defaultDir } from "../params";
import {
TxData,
CliGlobalOptions,
ListrContextBuildAndPublish
} from "../types";
import { Github } from "../providers/github/Github";
/**
* Create (or edit) a Github release, then upload all assets
*/
export function createGithubRelease({
dir = defaultDir,
buildDir,
releaseMultiHash,
verbose,
silent
}: {
buildDir: string;
releaseMultiHash: string;
} & CliGlobalOptions): Listr<ListrContextBuildAndPublish> {
// OAuth2 token from Github
if (!process.env.GITHUB_TOKEN)
throw Error("GITHUB_TOKEN ENV (OAuth2) is required");
const github = Github.fromDir(dir);
// Gather repo data, repoSlug = "dappnode/DNP_ADMIN"
const repoSlug =
getRepoSlugFromManifest(dir) ||
process.env.TRAVIS_REPO_SLUG ||
process.env.GITHUB_REPOSITORY ||
"";
const [owner, repo] = repoSlug.split("/");
if (!repoSlug)
throw Error(
"manifest.repository must be properly defined to create a Github release"
);
if (!owner) throw Error(`repoSlug "${repoSlug}" hasn't an owner`);
if (!repo) throw Error(`repoSlug "${repoSlug}" hasn't a repo`);
const isCi = process.env.CI;
const triggerTag = process.env.GITHUB_REF || process.env.TRAVIS_TAG;
return new Listr<ListrContextBuildAndPublish>(
[
// 1. Handle tags
// - If the release is triggered in CI,
// the trigger tag must be remove and replaced by the release tag
// - If the release is triggered locally, the commit should be
// tagged and released on that tag
{
title: `Handle tags`,
task: async (ctx, task) => {
// Sanity check, make sure repo exists
await github.assertRepoExists();
// Get next version from context
if (!ctx.nextVersion) throw Error("Missing ctx.nextVersion");
const tag = `v${ctx.nextVersion}`;
// If the release is triggered in CI,
// the trigger tag must be removed ("release/patch")
if (isCi && triggerTag && triggerTag.startsWith("release"))
await github.deleteTagIfExists(triggerTag);
// Check if the release tag exists remotely. If so, remove it
await github.deleteTagIfExists(tag);
// Get the commit sha to be tagged
// - If on CI, use the current commit
// - Otherwise use the current HEAD commit
const currentCommitSha =
process.env.GITHUB_SHA ||
process.env.TRAVIS_COMMIT ||
(await getGitHead()).commit;
// Tag the current commit with the release tag
task.output = `Releasing commit ${currentCommitSha} at tag ${tag}`;
await github.createTag(tag, currentCommitSha);
}
},
// 2. Create release
// - nextVersion comes from the first task in `publish`
// - buildDir comes from the first task in `publish`
{
title: `Create release`,
task: async (ctx, task) => {
// console.log(res);
// Get next version from context, fir
const { nextVersion, txData } = ctx;
if (!nextVersion) throw Error("Missing ctx.nextVersion");
const tag = `v${nextVersion}`;
// Delete all releases that have the name tag or name
// If there are no releases, repos.listReleases will return []
task.output = "Deleting existing release...";
await github.deteleReleaseAndAssets(tag);
// Plain text file with should contain the IPFS hash of the release
// Necessary for the installer script to fetch the latest content hash
// of the eth clients. The resulting hashes are used by the DAPPMANAGER
// to install an eth client when the user does not want to use a remote node
const contentHashPath = path.join(buildDir, contentHashFile);
fs.writeFileSync(contentHashPath, releaseMultiHash);
task.output = `Creating release for tag ${tag}...`;
await github.createReleaseAndUploadAssets(tag, {
body: getReleaseBody(txData),
// Tag as pre-release until it is actually published in APM mainnet
prerelease: true,
assetsDir: buildDir,
// Used to ignore duplicated legacy .tar.xz image
ignorePattern: /\.tar\.xz$/
});
// Clean content hash file so the directory uploaded to IPFS is the same
// as the local build_* dir. User can then `ipfs add -r` and get the same hash
fs.unlinkSync(contentHashPath);
}
}
],
{ renderer: verbose ? "verbose" : silent ? "silent" : "default" }
);
}
// Utils
/**
* Write the release body
* #### TODO: Extend this to automatically write the body
*/
function getReleaseBody(txData: TxData) {
const link = getPublishTxLink(txData);
const changelog = "";
const installLink = getInstallDnpLink(txData.releaseMultiHash);
return `
##### Changelog
${changelog}
---
##### For package mantainer
Authorized developer account may execute this transaction [from a pre-filled link](${link})[.](${installLink})
<details><summary>Release details</summary>
<p>
\`\`\`
To: ${txData.to}
Value: ${txData.value}
Data: ${txData.data}
Gas limit: ${txData.gasLimit}
\`\`\`
\`\`\`
${txData.releaseMultiHash}
\`\`\`
</p>
</details>
`.trim();
}