1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | import {
|
18 | GitHubRepoRef,
|
19 | GitProject,
|
20 | logger,
|
21 | ProjectOperationCredentials,
|
22 | RemoteRepoRef,
|
23 | } from "@atomist/automation-client";
|
24 | import {
|
25 | ExecuteGoal,
|
26 | ExecuteGoalResult,
|
27 | GoalInvocation,
|
28 | LoggingProgressLog,
|
29 | ProgressLog,
|
30 | spawnLog,
|
31 | } from "@atomist/sdm";
|
32 | import {
|
33 | createTag,
|
34 | createTagReference,
|
35 | Tag,
|
36 | } from "../../../util/github/ghub";
|
37 | import { goalInvocationVersion } from "./local/projectVersioner";
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | export interface ExecuteTagOptions {
|
45 | |
46 |
|
47 |
|
48 |
|
49 | message?: string;
|
50 |
|
51 | name?: string;
|
52 | |
53 |
|
54 |
|
55 |
|
56 | release?: boolean;
|
57 | |
58 |
|
59 |
|
60 |
|
61 | build?: string;
|
62 | }
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 | export function executeTag(opts: ExecuteTagOptions = {}): ExecuteGoal {
|
72 | return async (goalInvocation: GoalInvocation): Promise<ExecuteGoalResult> => {
|
73 | const { configuration, goalEvent, credentials, id, context, progressLog } = goalInvocation;
|
74 |
|
75 | return configuration.sdm.projectLoader.doWithProject({ credentials, id, context, readOnly: false }, async project => {
|
76 | try {
|
77 | let tag: string;
|
78 | let message: string;
|
79 | if (opts.message) {
|
80 | message = opts.message;
|
81 | } else if (goalEvent.push.after && goalEvent.push.after.message) {
|
82 | message = goalEvent.push.after.message.split("\n")[0];
|
83 | }
|
84 | if (opts.name) {
|
85 | tag = opts.name;
|
86 | message = message || `Tag ${opts.name}`;
|
87 | } else {
|
88 | const version = await goalInvocationVersion(goalInvocation);
|
89 | if (opts.release) {
|
90 | tag = version.replace(/[-+].*/, "");
|
91 | message = message || `Release ${tag}`;
|
92 | } else {
|
93 | tag = version;
|
94 | message = message || `Prerelease ${tag}`;
|
95 | }
|
96 | }
|
97 | if (opts.build) {
|
98 | tag += "+" + opts.build;
|
99 | }
|
100 | await createGitTag({ project, tag, message, log: progressLog });
|
101 | return { code: 0, message: `Created tag '${tag}' for ${goalEvent.repo.owner}/${goalEvent.repo.name}` };
|
102 | } catch (e) {
|
103 | const message = `Failed to create tag for ${goalEvent.repo.owner}/${goalEvent.repo.name}: ${e.message}`;
|
104 | logger.error(message);
|
105 | progressLog.write(message);
|
106 | return { code: 1, message };
|
107 | }
|
108 | });
|
109 | };
|
110 | }
|
111 |
|
112 |
|
113 | export interface CreateGitTagOptions {
|
114 |
|
115 | project: GitProject;
|
116 |
|
117 | tag: string;
|
118 |
|
119 | message?: string;
|
120 |
|
121 | log?: ProgressLog;
|
122 | }
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | export async function createGitTag(opts: CreateGitTagOptions): Promise<void> {
|
130 | if (!opts.tag) {
|
131 | throw new Error("You must provide a valid Git tag");
|
132 | }
|
133 | if (!opts.project) {
|
134 | throw new Error("You must provide a Git project");
|
135 | }
|
136 | if (!opts.log) {
|
137 | opts.log = new LoggingProgressLog("logger");
|
138 | }
|
139 | const remote = opts.project.remote || "origin";
|
140 | try {
|
141 | const spawnOpts = { cwd: opts.project.baseDir, log: opts.log };
|
142 | const tagArgs = ["tag", opts.tag];
|
143 | if (opts.message) {
|
144 | tagArgs.splice(1, 0, "-m", opts.message);
|
145 | }
|
146 | const tagResult = await spawnLog("git", tagArgs, spawnOpts);
|
147 | if (tagResult.code) {
|
148 | throw new Error(`git tag failed: ${tagResult.message}`);
|
149 | }
|
150 | const pushResult = await spawnLog("git", ["push", remote, opts.tag], spawnOpts);
|
151 | if (pushResult.code) {
|
152 | throw new Error(`git push failed: ${pushResult.message}`);
|
153 | }
|
154 | } catch (e) {
|
155 | e.message = `Failed to create and push git tag '${opts.tag}': ${e.message}`;
|
156 | throw e;
|
157 | }
|
158 | }
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | export async function createTagForStatus(id: RemoteRepoRef,
|
171 | sha: string,
|
172 | message: string,
|
173 | version: string,
|
174 | credentials: ProjectOperationCredentials): Promise<void> {
|
175 | const tag: Tag = {
|
176 | tag: version,
|
177 | message,
|
178 | object: sha,
|
179 | type: "commit",
|
180 | tagger: {
|
181 | name: "Atomist",
|
182 | email: "info@atomist.com",
|
183 | date: new Date().toISOString(),
|
184 | },
|
185 | };
|
186 |
|
187 | await createTag(credentials, id as GitHubRepoRef, tag);
|
188 | await createTagReference(credentials, id as GitHubRepoRef, tag);
|
189 | }
|