Skip to content
This repository was archived by the owner on Feb 12, 2026. It is now read-only.

Commit 7d300c1

Browse files
committed
SIGN-8057 Change action to only talk with the connector
1 parent 22bcc18 commit 7d300c1

10 files changed

Lines changed: 155 additions & 120 deletions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// TODO: write tests
2+
3+
export class ConnectorUrlBuilder {
4+
private readonly apiVersion: string = "1.0";
5+
private readonly baseSigningRequestsRoute: string;
6+
7+
constructor(private readonly connectorBaseUrl: string, private readonly organizationId: string) {
8+
this.connectorBaseUrl = this.trimSlash(this.connectorBaseUrl);
9+
this.baseSigningRequestsRoute = `${this.connectorBaseUrl}/${encodeURIComponent(this.organizationId)}/SigningRequests`
10+
}
11+
12+
public buildSubmitSigningRequestUrl(): string {
13+
return `${this.baseSigningRequestsRoute}?api-version=${this.apiVersion}`
14+
}
15+
16+
public buildGetSigningRequestStatusUrl(signingRequestId: string): string {
17+
return `${this.baseSigningRequestsRoute}/${encodeURIComponent(signingRequestId)}/Status?api-version=${this.apiVersion}`
18+
}
19+
20+
public buildGetSignedArtifactUrl(signingRequestId: string): string {
21+
return `${this.baseSigningRequestsRoute}/${encodeURIComponent(signingRequestId)}/SignedArtifact?api-version=${this.apiVersion}`
22+
}
23+
24+
private trimSlash(text: string): string {
25+
if (text && text[text.length - 1] === '/') {
26+
return text.substring(0, text.length - 1);
27+
}
28+
return text;
29+
}
30+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface SigningRequestStatusDto {
2+
status: string;
3+
isFinalStatus: boolean;
4+
webLink: string;
5+
hasArtifactBeenDownloadedBySignPathInCaseOfArtifactRetrieval: boolean;
6+
}

actions/submit-signing-request/dtos/signing-request.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

actions/submit-signing-request/helper-input-output.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export class HelperInputOutput {
6060
return getInputNumber('service-unavailable-timeout-in-seconds', { required: true });
6161
}
6262

63+
// TODO: change to connector right?
6364
setSignedArtifactDownloadUrl(url: string):void {
6465
core.setOutput('signed-artifact-download-url', url);
6566
}
@@ -72,6 +73,7 @@ export class HelperInputOutput {
7273
core.setOutput('signing-request-web-url', signingRequestUrl);
7374
}
7475

76+
// TODO: drop?
7577
setSignPathApiUrl(signingRequestUrl: string): void {
7678
core.setOutput('signpath-api-url', signingRequestUrl);
7779
}

actions/submit-signing-request/signpath-url-builder.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

actions/submit-signing-request/task.ts

Lines changed: 45 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@ import axios, { AxiosError, AxiosResponse } from 'axios';
22
import axiosRetry from 'axios-retry';
33
import * as core from '@actions/core';
44
import * as moment from 'moment';
5-
import url from 'url';
65

76
import { LogEntry, LogLevelDebug, LogLevelError, LogLevelInformation, LogLevelWarning, SubmitSigningRequestResult, ValidationResult } from './dtos/submit-signing-request-result';
87
import { buildSignPathAuthorizationHeader, executeWithRetries, httpErrorResponseToText } from './utils';
9-
import { SignPathUrlBuilder } from './signpath-url-builder';
10-
import { SigningRequestDto } from './dtos/signing-request';
8+
import { ConnectorUrlBuilder } from './connector-url-builder';
119
import { HelperInputOutput } from './helper-input-output';
1210
import { taskVersion } from './version';
1311
import { HelperArtifactDownload } from './helper-artifact-download';
1412
import { Config } from './config';
13+
import { SigningRequestStatusDto } from './dtos/signing-request-status';
1514

1615
// output variables
1716
// signingRequestId - the id of the newly created signing request
@@ -20,13 +19,14 @@ import { Config } from './config';
2019
// signingRequestDownloadUrl - the url of the signed artifact in SignPath
2120

2221
export class Task {
23-
urlBuilder: SignPathUrlBuilder;
22+
urlBuilder: ConnectorUrlBuilder;
2423

25-
constructor (
24+
constructor(
2625
private helperInputOutput: HelperInputOutput,
2726
private helperArtifactDownload: HelperArtifactDownload,
28-
private config: Config) {
29-
this.urlBuilder = new SignPathUrlBuilder(this.helperInputOutput.signPathConnectorUrl);
27+
private config: Config
28+
) {
29+
this.urlBuilder = new ConnectorUrlBuilder(this.helperInputOutput.signPathConnectorUrl, this.helperInputOutput.organizationId);
3030
}
3131

3232
async run() {
@@ -37,11 +37,10 @@ export class Task {
3737
const signingRequestId = await this.submitSigningRequest();
3838

3939
if (this.helperInputOutput.waitForCompletion) {
40-
const signingRequest = await this.ensureSigningRequestCompleted(signingRequestId);
41-
this.helperInputOutput.setSignedArtifactDownloadUrl(signingRequest.signedArtifactLink);
40+
await this.ensureSigningRequestCompleted(signingRequestId);
4241

43-
if(this.helperInputOutput.outputArtifactDirectory) {
44-
await this.helperArtifactDownload.downloadSignedArtifact(signingRequest.signedArtifactLink);
42+
if (this.helperInputOutput.outputArtifactDirectory) {
43+
await this.helperArtifactDownload.downloadSignedArtifact(this.urlBuilder.buildGetSignedArtifactUrl(signingRequestId));
4544
}
4645
}
4746
else {
@@ -53,7 +52,7 @@ export class Task {
5352
}
5453
}
5554

56-
private async submitSigningRequest (): Promise<string> {
55+
private async submitSigningRequest(): Promise<string> {
5756

5857
core.info('Submitting the signing request to SignPath CI connector...');
5958

@@ -63,15 +62,20 @@ export class Task {
6362
// call the signPath API to submit the signing request
6463
const response = (await axios
6564
.post<SubmitSigningRequestResult>(this.urlBuilder.buildSubmitSigningRequestUrl(),
66-
submitRequestPayload,
67-
{ responseType: "json" })
65+
submitRequestPayload,
66+
{
67+
responseType: "json",
68+
headers: {
69+
"Authorization": buildSignPathAuthorizationHeader(this.helperInputOutput.signPathApiToken)
70+
}
71+
})
6872
.catch((e: AxiosError) => {
6973

70-
if(e.code === AxiosError.ERR_BAD_REQUEST) {
74+
if (e.code === AxiosError.ERR_BAD_REQUEST) {
7175

7276
const connectorResponse = e.response as AxiosResponse<SubmitSigningRequestResult>;
7377

74-
if(connectorResponse.data.error) {
78+
if (connectorResponse.data.error) {
7579
this.redirectConnectorLogsToActionLogs(connectorResponse.data.logs);
7680
// when an error occurs in the validator the error details are in the validationResult
7781
this.checkCiSystemValidationResult(connectorResponse.data.validationResult);
@@ -91,16 +95,15 @@ export class Task {
9195
this.redirectConnectorLogsToActionLogs(response.logs);
9296
this.checkCiSystemValidationResult(response.validationResult);
9397

94-
const signingRequestUrlObj = url.parse(response.signingRequestUrl);
95-
this.urlBuilder.signPathBaseUrl = signingRequestUrlObj.protocol + '//' + signingRequestUrlObj.host;
96-
9798
core.info(`SignPath signing request has been successfully submitted`);
9899
core.info(`The signing request id is ${response.signingRequestId}`);
99100
core.info(`You can view the signing request here: ${response.signingRequestUrl}`);
100101

101102
this.helperInputOutput.setSigningRequestId(response.signingRequestId);
102103
this.helperInputOutput.setSigningRequestWebUrl(response.signingRequestUrl);
103-
this.helperInputOutput.setSignPathApiUrl(this.urlBuilder.signPathBaseUrl + '/API');
104+
105+
// TODO: think what to set as output
106+
// this.helperInputOutput.setSignPathApiUrl(this.urlBuilder.signPathBaseUrl + '/API');
104107

105108
return response.signingRequestId;
106109
}
@@ -115,8 +118,7 @@ export class Task {
115118

116119
validationResult.errors.forEach(validationError => {
117120
core.error(`${validationError.error}`);
118-
if (validationError.howToFix)
119-
{
121+
if (validationError.howToFix) {
120122
core.info(validationError.howToFix);
121123
}
122124
});
@@ -127,16 +129,17 @@ export class Task {
127129
}
128130
}
129131

132+
// TODO: what the heck
130133
// if auto-generated GitHub Actions token (secrets.GITHUB_TOKEN) is used for artifact download,
131134
// ensure the workflow continues running until the download is complete.
132135
// The token is valid only for the workflow's duration
133136
private async ensureSignPathDownloadedUnsignedArtifact(signingRequestId: string): Promise<void> {
134137
core.info(`Waiting until SignPath downloaded the unsigned artifact...`);
135-
const requestData = await (executeWithRetries<SigningRequestDto>(
138+
const requestData = await (executeWithRetries<SigningRequestStatusDto>(
136139
async () => {
137-
const signingRequestDto = await (this.getSigningRequest(signingRequestId)
140+
const signingRequestDto = await (this.getSigningRequestStatus(signingRequestId)
138141
.then(data => {
139-
if(!data.unsignedArtifactLink && !data.isFinalStatus) {
142+
if (!data.hasArtifactBeenDownloadedBySignPathInCaseOfArtifactRetrieval && !data.isFinalStatus) {
140143
core.info(`Checking the download status: not yet complete`);
141144
// retry artifact download status check
142145
return { retry: true };
@@ -149,9 +152,9 @@ export class Task {
149152
this.config.CheckArtifactDownloadStatusIntervalInSeconds * 1000,
150153
this.config.CheckArtifactDownloadStatusIntervalInSeconds * 1000));
151154

152-
if (!requestData.unsignedArtifactLink) {
155+
if (!requestData.hasArtifactBeenDownloadedBySignPathInCaseOfArtifactRetrieval) {
153156

154-
if(!requestData.isFinalStatus) {
157+
if (!requestData.isFinalStatus) {
155158
const maxWaitingTime = moment.utc(this.helperInputOutput.waitForCompletionTimeoutInSeconds * 1000).format("hh:mm");
156159
core.error(`We have exceeded the maximum waiting time, which is ${maxWaitingTime}, and the GitHub artifact is still not downloaded by SignPath`);
157160
} else {
@@ -166,21 +169,21 @@ export class Task {
166169
// artifact already downloaded by SignPath
167170
}
168171

169-
private async ensureSigningRequestCompleted(signingRequestId: string): Promise<SigningRequestDto> {
172+
private async ensureSigningRequestCompleted(signingRequestId: string): Promise<SigningRequestStatusDto> {
170173
// check for status update
171174
core.info(`Checking the signing request status...`);
172-
const requestData = await (executeWithRetries<SigningRequestDto>(
175+
const requestData = await (executeWithRetries<SigningRequestStatusDto>(
173176
async () => {
174177

175-
const signingRequestDto = await (this.getSigningRequest(signingRequestId)
178+
const signingRequestStatusDto = await (this.getSigningRequestStatus(signingRequestId)
176179
.then(data => {
177-
if(data && !data.isFinalStatus) {
180+
if (data && !data.isFinalStatus) {
178181
core.info(`The signing request status is ${data.status}, which is not a final status; after a delay, we will check again...`);
179182
return { retry: true };
180183
}
181184
return { retry: false, result: data };
182185
}));
183-
return signingRequestDto;
186+
return signingRequestStatusDto;
184187
},
185188
this.helperInputOutput.waitForCompletionTimeoutInSeconds * 1000,
186189
this.config.MinDelayBetweenSigningRequestStatusChecksInSeconds * 1000,
@@ -200,12 +203,11 @@ export class Task {
200203
return requestData;
201204
}
202205

203-
private async getSigningRequest(signingRequestId: string): Promise<SigningRequestDto> {
204-
const requestStatusUrl = this.urlBuilder.buildGetSigningRequestUrl(
205-
this.helperInputOutput.organizationId, signingRequestId);
206+
private async getSigningRequestStatus(signingRequestId: string): Promise<SigningRequestStatusDto> {
207+
const requestStatusUrl = this.urlBuilder.buildGetSigningRequestStatusUrl(signingRequestId);
206208

207-
const signingRequestDto = await axios
208-
.get<SigningRequestDto>(
209+
const signingRequestStatusDto = await axios
210+
.get<SigningRequestStatusDto>(
209211
requestStatusUrl,
210212
{
211213
responseType: "json",
@@ -220,7 +222,8 @@ export class Task {
220222
throw new Error(httpErrorResponseToText(e));
221223
})
222224
.then(response => response.data);
223-
return signingRequestDto;
225+
226+
return signingRequestStatusDto;
224227
}
225228

226229
private configureAxios(): void {
@@ -249,22 +252,22 @@ export class Task {
249252
axiosRetry.isRetryableError = (error: AxiosError) => {
250253
let retryableHttpErrorCode = false;
251254

252-
if(error.response) {
253-
if(error.response.status === 502
255+
if (error.response) {
256+
if (error.response.status === 502
254257
|| error.response.status === 503
255258
|| error.response.status === 504) {
256259
retryableHttpErrorCode = true;
257260
core.info(`SignPath REST API is temporarily unavailable (server responded with ${error.response.status}).`);
258261
}
259262

260-
if(error.response.status === 429) {
263+
if (error.response.status === 429) {
261264
retryableHttpErrorCode = true;
262265
core.info('SignPath REST API encountered too many requests.');
263266
}
264267
}
265268

266269
return (error.code !== 'ECONNABORTED' &&
267-
(!error.response || retryableHttpErrorCode));
270+
(!error.response || retryableHttpErrorCode));
268271
}
269272

270273
// set retries
@@ -325,12 +328,10 @@ export class Task {
325328

326329
private buildSigningRequestPayload(): any {
327330
return {
328-
signPathApiToken: this.helperInputOutput.signPathApiToken,
329331
artifactId: this.helperInputOutput.githubArtifactId,
330332
gitHubWorkflowRunId: process.env.GITHUB_RUN_ID,
331333
gitHubRepository: process.env.GITHUB_REPOSITORY,
332334
gitHubToken: this.helperInputOutput.gitHubToken,
333-
signPathOrganizationId: this.helperInputOutput.organizationId,
334335
signPathProjectSlug: this.helperInputOutput.projectSlug,
335336
signPathSigningPolicySlug: this.helperInputOutput.signingPolicySlug,
336337
signPathArtifactConfigurationSlug: this.helperInputOutput.artifactConfigurationSlug,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as uuid from 'uuid';
2+
import { assert } from "chai";
3+
import { ConnectorUrlBuilder } from '../connector-url-builder';
4+
5+
const connectorUrl = "https://connector.com";
6+
const apiVersion = "1.0"
7+
const orgId = uuid.v4();
8+
9+
const sut = new ConnectorUrlBuilder(connectorUrl, orgId);
10+
11+
it("Should build submit signing request url correctly", () => {
12+
const expected = `${connectorUrl}/${orgId}/SigningRequests?api-version=${apiVersion}`
13+
const actual = sut.buildSubmitSigningRequestUrl();
14+
15+
assert.equal(actual, expected)
16+
})
17+
18+
it("Should build get signing request status url correctly", () => {
19+
const srId = uuid.v4();
20+
const expected = `${connectorUrl}/${orgId}/SigningRequests/${srId}/Status?api-version=${apiVersion}`
21+
const actual = sut.buildGetSigningRequestStatusUrl(srId);
22+
23+
assert.equal(actual, expected)
24+
})
25+
26+
it("Should build get signed artifact url correctly", () => {
27+
const srId = uuid.v4();
28+
const expected = `${connectorUrl}/${orgId}/SigningRequests/${srId}/SignedArtifact?api-version=${apiVersion}`
29+
const actual = sut.buildGetSignedArtifactUrl(srId);
30+
31+
assert.equal(actual, expected)
32+
})

actions/submit-signing-request/tests/helper-artifact-download.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { assert, expect } from "chai";
1+
import { assert } from "chai";
22
import { HelperArtifactDownload } from "../helper-artifact-download"
33
import * as path from 'path';
4-
import * as os from 'os';
54
import * as uuid from 'uuid';
65
import * as fs from 'fs'
76
import { HelperInputOutput } from "../helper-input-output";

0 commit comments

Comments
 (0)