Skip to content

Commit 8064c1b

Browse files
authored
Merge pull request #8 from j-ulrich/feature/7-check-for-code-updates
Feature/7 check for code updates
2 parents b0a0fd4 + b8b5fa2 commit 8064c1b

File tree

9 files changed

+486
-5
lines changed

9 files changed

+486
-5
lines changed

.github/QNetworkReplyErroCodes.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
NoError=0
2+
ConnectionRefusedError=1
3+
RemoteHostClosedError
4+
HostNotFoundError
5+
TimeoutError
6+
OperationCanceledError
7+
SslHandshakeFailedError
8+
TemporaryNetworkFailureError
9+
NetworkSessionFailedError
10+
BackgroundRequestNotAllowedError
11+
TooManyRedirectsError
12+
InsecureRedirectError
13+
UnknownNetworkError=99
14+
ProxyConnectionRefusedError=101
15+
ProxyConnectionClosedError
16+
ProxyNotFoundError
17+
ProxyTimeoutError
18+
ProxyAuthenticationRequiredError
19+
UnknownProxyError=199
20+
ContentAccessDenied=201
21+
ContentOperationNotPermittedError
22+
ContentNotFoundError
23+
AuthenticationRequiredError
24+
ContentReSendError
25+
ContentConflictError
26+
ContentGoneError
27+
UnknownContentError=299
28+
ProtocolUnknownError=301
29+
ProtocolInvalidOperationError
30+
ProtocolFailure=399
31+
InternalServerError=401
32+
OperationNotImplementedError
33+
ServiceUnavailableError
34+
UnknownServerError=499
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
const fetch = require( 'node-fetch' );
2+
const diff = require( 'diff' );
3+
const fs = require( 'fs' ).promises;
4+
const path = require( 'path' );
5+
const githubIssues = require( './githubIssues.js' );
6+
7+
let log = console;
8+
9+
const httpStatusCodesUrl = 'https://www.iana.org/assignments/http-status-codes/http-status-codes.txt';
10+
const issueTitleBase = 'IANA HTTP Status Code Update';
11+
12+
const checkForUpdate = async ( { github, core, context, dryRun } ) => {
13+
try
14+
{
15+
log = core;
16+
17+
const httpStatusCodes = await fetchHttpStatusCodesList();
18+
const lastUpdatedDate = httpStatusCodes.lastUpdated;
19+
const diffWithLastUsedVersion = await getDiffWithLastUsedVersion( httpStatusCodes.httpStatusCodesList );
20+
if ( !diffWithLastUsedVersion ) {
21+
log.info( 'HTTP status codes list is still up to date' );
22+
return;
23+
}
24+
log.warning( 'HTTP status codes list is outdated!' );
25+
26+
const existingGithubIssues = await githubIssues.searchForExistingGithubIssue( { keywords: [ issueTitleBase, lastUpdatedDate ], github, context } );
27+
28+
if ( existingGithubIssues.total_count === 0 ) {
29+
createNewGithubIssue( { lastUpdatedDate, diffWithLastUsedVersion, github, context, dryRun } );
30+
}
31+
else if ( existingGithubIssues.total_count === 1 ) {
32+
log.info( 'An issue already exists for this update.' );
33+
}
34+
else {
35+
log.warning( `Multiple issues exist for the HTTP status code update from ${lastUpdatedDate}:\n${ JSON.stringify( existingGithubIssues, undefined, 4 ) }` );
36+
}
37+
}
38+
catch ( error ) {
39+
core.setFailed( `Could not check for HTTP status codes updates: ${error}` );
40+
throw error;
41+
}
42+
};
43+
44+
const fetchHttpStatusCodesList = async () => {
45+
const response = await fetch( httpStatusCodesUrl );
46+
if ( !response.ok ) {
47+
throw Error( `Error fetching HTTP status codes list: ${response.status} ${response.statusText}` );
48+
}
49+
const httpStatusCodesList = await response.text();
50+
51+
const match = /Last Updated\s+(\d{4}-\d{2}-\d{2})/.exec( httpStatusCodesList );
52+
if ( !match ) {
53+
throw Error( 'Could not find "Last Updated" date in HTTP status list' );
54+
}
55+
const lastUpdated = match[ 1 ];
56+
return { lastUpdated, httpStatusCodesList };
57+
};
58+
59+
const getDiffWithLastUsedVersion = async ( httpStatusCodeList ) => {
60+
const pathToLastUsedVersion = path.resolve( './.github/http-status-codes.txt' );
61+
const lastUsedVersion = await fs.readFile( pathToLastUsedVersion, { encoding: 'utf-8' } );
62+
if ( lastUsedVersion === httpStatusCodeList ) {
63+
return null;
64+
}
65+
const patch = diff.createPatch( 'http-status-codes.txt', lastUsedVersion, httpStatusCodeList );
66+
return patch;
67+
};
68+
69+
const createNewGithubIssue = async ( { lastUpdatedDate, diffWithLastUsedVersion, github, context, dryRun } ) => {
70+
const title = `${issueTitleBase} ${lastUpdatedDate}`;
71+
const body = `The HTTP status codes list has been updated on ${lastUpdatedDate}. ` + '\n' +
72+
'See https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml' + '\n\n' +
73+
'## Diff ##' + '\n' +
74+
'```diff' + '\n' +
75+
diffWithLastUsedVersion + '\n' +
76+
'```';
77+
78+
if ( dryRun ) {
79+
log.info( `Would create issue:\n${ JSON.stringify( { title, body }, null, 4 ) }` );
80+
}
81+
else {
82+
const newIssue = await githubIssues.createNewGithubIssue( { title, body, github, context } );
83+
log.info( `Created issue #${newIssue.number}: ${newIssue.html_url}`);
84+
}
85+
};
86+
87+
const main = async () => {
88+
try
89+
{
90+
const httpStatusCodes = await fetchHttpStatusCodesList();
91+
log.log( httpStatusCodes.lastUpdated );
92+
const patch = await getDiffWithLastUsedVersion( httpStatusCodes.httpStatusCodesList );
93+
if ( patch ) {
94+
log.log( patch );
95+
}
96+
}
97+
catch( reason ) {
98+
log.error( reason );
99+
process.exitCode = -1;
100+
};
101+
};
102+
103+
module.exports = checkForUpdate;
104+
105+
if ( require.main === module ) {
106+
main();
107+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
const fetch = require( 'node-fetch' );
2+
const diff = require( 'diff' );
3+
const fs = require( 'fs' ).promises;
4+
const path = require( 'path' );
5+
const githubIssues = require( './githubIssues.js' );
6+
7+
let log = console;
8+
9+
const qNetworkReplyHeaderUrl = 'https://code.qt.io/cgit/qt/qtbase.git/plain/src/network/access/qnetworkreply.h';
10+
const issueTitleBase = 'QNetworkReply Error Code Update';
11+
12+
const checkForUpdate = async ( { github, core, context, dryRun } ) => {
13+
try
14+
{
15+
log = core;
16+
17+
const { blobId, qNetworkReplyErrorCodes } = await fetchQNetworkReplyErrorCodeListFromGitHub( github );
18+
const blobIdShort = shortenId( blobId );
19+
const diffWithLastUsedVersion = await getDiffWithLastUsedVersion( qNetworkReplyErrorCodes );
20+
if ( !diffWithLastUsedVersion ) {
21+
log.info( 'QNetworkReply error codes list is still up to date' );
22+
return;
23+
}
24+
log.warning( 'QNetworkReply error codes list is outdated!' );
25+
26+
const existingGithubIssues = await githubIssues.searchForExistingGithubIssue( { keywords: [ issueTitleBase, blobIdShort ], github, context } );
27+
28+
if ( existingGithubIssues.total_count === 0 ) {
29+
createNewGithubIssue( { blobId, diffWithLastUsedVersion, github, context, dryRun } );
30+
}
31+
else if ( existingGithubIssues.total_count === 1 ) {
32+
log.info( 'An issue already exists for this update.' );
33+
}
34+
else {
35+
log.warning( `Multiple issues exist for the QNetworkReply error codes update with id ${blobIdShort}:\n${ JSON.stringify( existingGithubIssues, undefined, 4 ) }` );
36+
}
37+
}
38+
catch ( error ) {
39+
core.setFailed( `Could not check for HTTP status codes updates: ${error}` );
40+
throw error;
41+
}
42+
};
43+
44+
const shortenId = id => {
45+
return id.substring( 0, 8 )
46+
};
47+
48+
const fetchQNetworkReplyErrorCodeListFromGitHub = async ( github ) => {
49+
const getContent = github.repos.getContent || github.repos.getContents;
50+
const response = await getContent( {
51+
owner: 'qt',
52+
repo: 'qtbase',
53+
path: 'src/network/access/qnetworkreply.h',
54+
ref: 'dev'
55+
} );
56+
const blobId = response.data.sha;
57+
58+
const qNetworkReplyHeaderSource = decodeRepoContent( response.data );
59+
const qNetworkReplyErrorCodes = extractQNetworkReplyErrorCodes( qNetworkReplyHeaderSource );
60+
61+
return { blobId, qNetworkReplyErrorCodes };
62+
};
63+
64+
const fetchQNetworkReplyErrorCodeListFromQt = async () => {
65+
const response = await fetch( qNetworkReplyHeaderUrl );
66+
if ( !response.ok ) {
67+
throw Error( `Error fetching QNetworkReply header: ${response.status} ${response.statusText}` );
68+
}
69+
const qNetworkReplyHeaderSource = await response.text();
70+
71+
return extractQNetworkReplyErrorCodes( qNetworkReplyHeaderSource );
72+
}
73+
74+
const decodeRepoContent = ( response ) => {
75+
try {
76+
return Buffer.from( response.content, response.encoding ).toString( 'utf-8' );
77+
}
78+
catch( e ) {
79+
throw Error( `Unsupported repository content encoding: ${response.encoding}\nOriginal exception: ${e}` );
80+
}
81+
};
82+
83+
const extractQNetworkReplyErrorCodes = ( qnetworkReplyHeaderSource ) => {
84+
const enumMatch = /enum NetworkError {(.*?)};/s.exec( qnetworkReplyHeaderSource );
85+
if ( !enumMatch ) {
86+
throw Error( 'Could not extract NetworkError codes from QNetworkReply header' );
87+
}
88+
const enums = enumMatch[ 1 ].trim().replace( /\/\/.*$|[ \t]+|\n\n+|,/mg, '' );
89+
return enums;
90+
}
91+
92+
const getDiffWithLastUsedVersion = async ( qNetworkReplyErrorCodes ) => {
93+
const pathToLastUsedVersion = path.resolve( './.github/QNetworkReplyErroCodes.txt' );
94+
const lastUsedVersion = await fs.readFile( pathToLastUsedVersion, { encoding: 'utf-8' } );
95+
if ( lastUsedVersion === qNetworkReplyErrorCodes ) {
96+
return null;
97+
}
98+
const patch = diff.createPatch( 'QNetworkReplyErroCodes.txt', lastUsedVersion, qNetworkReplyErrorCodes );
99+
return patch;
100+
};
101+
102+
const createNewGithubIssue = async ( { blobId, diffWithLastUsedVersion, github, context, dryRun } ) => {
103+
const blobIdShort = shortenId( blobId );
104+
const title = `${issueTitleBase} ${blobIdShort}`;
105+
const body = 'The `QNetworkReply::NetworkError` codes list has been updated. \n' +
106+
'See https://code.qt.io/cgit/qt/qtbase.git/log/src/network/access/qnetworkreply.h' + '\n\n' +
107+
'## Diff ##' + '\n' +
108+
'```diff' + '\n' +
109+
diffWithLastUsedVersion + '\n' +
110+
'```';
111+
112+
if ( dryRun ) {
113+
log.info( `Would create issue:\n${ JSON.stringify( { title, body }, null, 4 ) }` );
114+
}
115+
else {
116+
const newIssue = await githubIssues.createNewGithubIssue( { title, body, github, context } );
117+
log.info( `Created issue #${newIssue.number}: ${newIssue.html_url}`);
118+
}
119+
};
120+
121+
const main = async () => {
122+
try
123+
{
124+
const qnetworkReplyErrorCodes = await fetchQNetworkReplyErrorCodeListFromQt();
125+
const patch = await getDiffWithLastUsedVersion( qnetworkReplyErrorCodes );
126+
if ( patch ) {
127+
log.log( patch );
128+
}
129+
else {
130+
log.log( "Last used version is still up to date!" );
131+
}
132+
}
133+
catch( reason ) {
134+
log.error( reason );
135+
process.exitCode = -1;
136+
};
137+
};
138+
139+
module.exports = checkForUpdate;
140+
141+
if ( require.main === module ) {
142+
main();
143+
}

.github/githubIssues.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
const searchForExistingGithubIssue = async ( { keywords, github, context } ) => {
3+
const query = keywords.concat( [ 'in:title', `repo:${context.payload.repository.full_name}`, 'type:issue' ] ).join( '+' );
4+
const searchResponse = await github.search.issuesAndPullRequests( {
5+
q: query,
6+
} );
7+
return searchResponse.data;
8+
};
9+
10+
const createNewGithubIssue = async ( { title, body, github, context } ) => {
11+
12+
const newIssue = {
13+
owner: context.repo.owner,
14+
repo: context.repo.repo,
15+
title,
16+
body
17+
};
18+
19+
const issueResponse = await github.issues.create( newIssue );
20+
return issueResponse.data;
21+
};
22+
23+
module.exports = { searchForExistingGithubIssue, createNewGithubIssue };

0 commit comments

Comments
 (0)