Skip to content

Commit 5ca24c5

Browse files
committed
ci(github): automate check and create PR for device authenticity
1 parent b209705 commit 5ca24c5

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: [Check] Update Config and Create PR
2+
3+
on:
4+
schedule:
5+
# Runs at midnight UTC every day at 01:00 AM CET
6+
- cron: "0 0 * * *"
7+
workflow_dispatch:
8+
# TODO: this is just to test it.
9+
pull_request:
10+
paths:
11+
- ".github/workflows/update-connect-config.yml"
12+
13+
jobs:
14+
update-config:
15+
runs-on: ubuntu-latest
16+
if: github.repository == 'trezor/trezor-suite'
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
with:
21+
token: ${{ secrets.TREZOR_BOT_TOKEN }}
22+
23+
- name: Setup node
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version-file: ".nvmrc"
27+
28+
- name: Install dependencies
29+
run: yarn install --immutable
30+
31+
- name: Setup Git config
32+
run: |
33+
git config --global user.name "trezor-ci"
34+
git config --global user.email "${{ secrets.TREZOR_BOT_EMAIL }}"
35+
36+
- name: Check for changes and create PR
37+
run: yarn tsx ./scripts/ci/check-connect-data.ts

scripts/ci/check-connect-data.ts

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import fetch from 'cross-fetch';
2+
import fs from 'fs-extra';
3+
import path from 'path';
4+
import { commit } from './helpers';
5+
6+
const { exec } = require('./helpers');
7+
8+
const AUTHENTICITY_BASE_URL = 'https://data.trezor.io';
9+
const authenticityPaths = {
10+
T2B1: {
11+
authenticity: 'firmware/t2b1/authenticity.json',
12+
authenticityDev: 'firmware/t2b1/authenticity-dev.json',
13+
},
14+
T3T1: {
15+
authenticity: 'firmware/t3t1/authenticity.json',
16+
authenticityDev: 'firmware/t3t1/authenticity-dev.json',
17+
},
18+
};
19+
20+
const ROOT = path.join(__dirname, '..', '..');
21+
const CONFIG_FILE_PATH = path.join(ROOT, 'packages/connect/src/data/deviceAuthenticityConfig.ts');
22+
23+
type AuthenticityPathsKeys = keyof typeof authenticityPaths;
24+
25+
const fetchJSON = async (url: string) => {
26+
const response = await fetch(url);
27+
if (!response.ok) {
28+
throw new Error(`Failed to fetch ${url}: ${response.statusText}`);
29+
}
30+
return response.json();
31+
};
32+
33+
const updateConfigFromJSON = async () => {
34+
try {
35+
const devicesKeys = Object.keys(authenticityPaths) as AuthenticityPathsKeys[];
36+
37+
// Import the current configuration object
38+
let { deviceAuthenticityConfig } = require(CONFIG_FILE_PATH);
39+
40+
for (const deviceKey of devicesKeys) {
41+
const { authenticity, authenticityDev } = authenticityPaths[deviceKey];
42+
const authenticityUrl = `${AUTHENTICITY_BASE_URL}/${authenticity}`;
43+
const authenticityData = await fetchJSON(authenticityUrl);
44+
const authenticityDevUrl = `${AUTHENTICITY_BASE_URL}/${authenticityDev}`;
45+
const authenticityDevData = await fetchJSON(authenticityDevUrl);
46+
47+
deviceAuthenticityConfig[deviceKey] = {
48+
rootPubKeys: authenticityData.root_pubkeys,
49+
caPubKeys: authenticityData.ca_pubkeys,
50+
debug: {
51+
rootPubKeys: authenticityDevData.root_pubkeys,
52+
caPubKeys: authenticityDevData.ca_pubkeys,
53+
},
54+
};
55+
}
56+
57+
const updatedConfigString = `
58+
/** THIS FILE IS AUTOMATICALLY UPDATED by script ci/scripts/check-connect-data.ts */
59+
import { DeviceAuthenticityConfig } from './deviceAuthenticityConfigTypes';
60+
61+
/**
62+
* How to update this config or check Sentry "Device authenticity invalid!" error? Please read this internal description:
63+
* https://www.notion.so/satoshilabs/Device-authenticity-check-b8656a0fe3ab4a0d84c61534a73de462?pvs=4
64+
*/
65+
export const deviceAuthenticityConfig: DeviceAuthenticityConfig = ${JSON.stringify(deviceAuthenticityConfig, null, 4)};
66+
`;
67+
68+
await fs.writeFile(CONFIG_FILE_PATH, updatedConfigString);
69+
70+
await exec('yarn', ['prettier', '--write', CONFIG_FILE_PATH]);
71+
72+
console.log('Configuration updated successfully.');
73+
74+
console.log('Checking if there were changes.');
75+
const changes = await exec('git', ['diff', CONFIG_FILE_PATH]);
76+
if (changes.stdout !== '') {
77+
console.log('There were changes in keys.');
78+
// There were changes in CONFIG_FILE_PATH
79+
const hashChanges = await exec('git', [
80+
'log',
81+
'-1',
82+
'--pretty=format:"%H"',
83+
'--',
84+
CONFIG_FILE_PATH,
85+
]);
86+
// We use the hash of the changes to create branch name to avoid use a branch that already exists.
87+
const branchName = `chore/update-device-authenticity-config-${hashChanges.stdout.replace(/"/g, '')}`;
88+
const commitMessage = 'chore(connect): update device authenticity config';
89+
await exec('git', ['checkout', '-b', branchName]);
90+
commit({
91+
path: ROOT,
92+
message: commitMessage,
93+
});
94+
await exec('git', ['push', 'origin', branchName]);
95+
96+
await exec('gh', [
97+
'pr',
98+
'create',
99+
'--repo',
100+
'trezor/trezor-suite',
101+
'--title',
102+
`${commitMessage}`,
103+
'--body-file',
104+
'docs/packages/connect/check-connect-data.md',
105+
'--base',
106+
'develop',
107+
'--head',
108+
branchName,
109+
]);
110+
}
111+
} catch (error) {
112+
console.error(`Error updating configuration: ${error.message}`);
113+
}
114+
};
115+
116+
updateConfigFromJSON();

0 commit comments

Comments
 (0)