3 Commits

Author SHA1 Message Date
fe95766606 Add ssh key sign 2025-03-25 04:55:56 +00:00
362593798c use loose moves for verifing movable packages 2025-03-15 13:13:52 -05:00
362656d13e retry failed webhooks 2025-02-08 23:21:42 -05:00
6 changed files with 125 additions and 46 deletions

20
.vscode/launch.json vendored
View File

@@ -5,16 +5,30 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"type": "pwa-node", "type": "node",
"request": "launch", "request": "launch",
"name": "Launch Program", "name": "Launch Program",
"skipFiles": [ "skipFiles": [
"<node_internals>/**" "<node_internals>/**"
], ],
"env": { "env": {
"JOB": "jobs/example.json5" "GPGPASS": "${input:gpgpass}"
}, },
"program": "${workspaceFolder}/index.js" "program": "${workspaceFolder}/bin/artix-metro.mjs",
"args": [
"--job",
"jobs/kde-01.json5"
],
"outFiles": [
"${workspaceFolder}/distribution/*"
]
}
],
"inputs": [
{
"id": "gpgpass",
"type": "promptString",
"description": "Enter your GPG password"
} }
] ]
} }

View File

@@ -57,6 +57,7 @@ _artix_metro_completion() {
COMPREPLY=() COMPREPLY=()
;; ;;
"-j"|"--job") "-j"|"--job")
compopt -o filenames
COMPREPLY=( $(compgen -f -- "$cur") ) COMPREPLY=( $(compgen -f -- "$cur") )
;; ;;
"--workspace") "--workspace")

60
package-lock.json generated
View File

@@ -1,19 +1,19 @@
{ {
"name": "artix-metro", "name": "artix-metro",
"version": "3.1.0", "version": "3.1.2",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "artix-metro", "name": "artix-metro",
"version": "3.1.0", "version": "3.1.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"artix-checkupdates": "1.0.1", "artix-checkupdates": "1.0.2",
"cli-color": "2.0.4", "cli-color": "2.0.4",
"glob": "11.0.1", "glob": "11.0.1",
"json5": "2.2.3", "json5": "2.2.3",
"ky": "1.7.4" "ky": "1.7.5"
}, },
"bin": { "bin": {
"artix-metro": "bin/artix-metro.mjs" "artix-metro": "bin/artix-metro.mjs"
@@ -21,8 +21,8 @@
"devDependencies": { "devDependencies": {
"@sindresorhus/tsconfig": "7.0.0", "@sindresorhus/tsconfig": "7.0.0",
"@types/cli-color": "2.0.6", "@types/cli-color": "2.0.6",
"@types/node": "22.10.7", "@types/node": "22.13.10",
"typescript": "5.7.3" "typescript": "5.8.2"
} }
}, },
"node_modules/@isaacs/cliui": { "node_modules/@isaacs/cliui": {
@@ -63,9 +63,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "22.10.7", "version": "22.13.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
"integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -97,9 +97,9 @@
} }
}, },
"node_modules/artix-checkupdates": { "node_modules/artix-checkupdates": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/artix-checkupdates/-/artix-checkupdates-1.0.1.tgz", "resolved": "https://registry.npmjs.org/artix-checkupdates/-/artix-checkupdates-1.0.2.tgz",
"integrity": "sha512-C0AxI3SfoUUekYg3ft/y4+tRjIe8uuDLvBQRSmkfNsRuBLNlEHaxhY/93JB6G4AGXp5RFLZYDnbgjakgbTLhkw==", "integrity": "sha512-VTLeMVysGeZ3QiLtEyWoVM8D57YyeYhYQoL3vcW9b0dZE+zhE4di80PYEFGeQzIBcCStcBFnI1uSBoAEHMVHAQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/balanced-match": { "node_modules/balanced-match": {
@@ -357,9 +357,9 @@
} }
}, },
"node_modules/ky": { "node_modules/ky": {
"version": "1.7.4", "version": "1.7.5",
"resolved": "https://registry.npmjs.org/ky/-/ky-1.7.4.tgz", "resolved": "https://registry.npmjs.org/ky/-/ky-1.7.5.tgz",
"integrity": "sha512-zYEr/gh7uLW2l4su11bmQ2M9xLgQLjyvx58UyNM/6nuqyWFHPX5ktMjvpev3F8QWdjSsHUpnWew4PBCswBNuMQ==", "integrity": "sha512-HzhziW6sc5m0pwi5M196+7cEBtbt0lCYi67wNsiwMUmz833wloE0gbzJPWKs1gliFKQb34huItDQX97LyOdPdA==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=18" "node": ">=18"
@@ -604,9 +604,9 @@
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.7.3", "version": "5.8.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
@@ -758,9 +758,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "22.10.7", "version": "22.13.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
"integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==", "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
"dev": true, "dev": true,
"requires": { "requires": {
"undici-types": "~6.20.0" "undici-types": "~6.20.0"
@@ -777,9 +777,9 @@
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
}, },
"artix-checkupdates": { "artix-checkupdates": {
"version": "1.0.1", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/artix-checkupdates/-/artix-checkupdates-1.0.1.tgz", "resolved": "https://registry.npmjs.org/artix-checkupdates/-/artix-checkupdates-1.0.2.tgz",
"integrity": "sha512-C0AxI3SfoUUekYg3ft/y4+tRjIe8uuDLvBQRSmkfNsRuBLNlEHaxhY/93JB6G4AGXp5RFLZYDnbgjakgbTLhkw==" "integrity": "sha512-VTLeMVysGeZ3QiLtEyWoVM8D57YyeYhYQoL3vcW9b0dZE+zhE4di80PYEFGeQzIBcCStcBFnI1uSBoAEHMVHAQ=="
}, },
"balanced-match": { "balanced-match": {
"version": "1.0.2", "version": "1.0.2",
@@ -982,9 +982,9 @@
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
}, },
"ky": { "ky": {
"version": "1.7.4", "version": "1.7.5",
"resolved": "https://registry.npmjs.org/ky/-/ky-1.7.4.tgz", "resolved": "https://registry.npmjs.org/ky/-/ky-1.7.5.tgz",
"integrity": "sha512-zYEr/gh7uLW2l4su11bmQ2M9xLgQLjyvx58UyNM/6nuqyWFHPX5ktMjvpev3F8QWdjSsHUpnWew4PBCswBNuMQ==" "integrity": "sha512-HzhziW6sc5m0pwi5M196+7cEBtbt0lCYi67wNsiwMUmz833wloE0gbzJPWKs1gliFKQb34huItDQX97LyOdPdA=="
}, },
"lru-cache": { "lru-cache": {
"version": "11.0.2", "version": "11.0.2",
@@ -1147,9 +1147,9 @@
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
}, },
"typescript": { "typescript": {
"version": "5.7.3", "version": "5.8.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"dev": true "dev": true
}, },
"undici-types": { "undici-types": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "artix-metro", "name": "artix-metro",
"version": "3.1.0", "version": "3.1.2",
"description": "Automate pushing packages to Artix", "description": "Automate pushing packages to Artix",
"keywords": [ "keywords": [
"artix", "artix",
@@ -31,16 +31,16 @@
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"artix-checkupdates": "1.0.1", "artix-checkupdates": "1.0.2",
"cli-color": "2.0.4", "cli-color": "2.0.4",
"ky": "1.7.4", "ky": "1.7.5",
"json5": "2.2.3", "json5": "2.2.3",
"glob": "11.0.1" "glob": "11.0.1"
}, },
"devDependencies": { "devDependencies": {
"typescript": "5.7.3", "typescript": "5.8.2",
"@sindresorhus/tsconfig": "7.0.0", "@sindresorhus/tsconfig": "7.0.0",
"@types/cli-color": "2.0.6", "@types/cli-color": "2.0.6",
"@types/node": "22.10.7" "@types/node": "22.13.10"
} }
} }

View File

@@ -1,7 +1,7 @@
import ky from 'ky'; import ky from 'ky';
import { snooze } from './snooze.mjs'; import { snooze } from './snooze.mjs';
type CiStatus = "pending" | "success" | "error" | "failure"; type CiStatus = "pending" | "success" | "error" | "failure" | "";
interface GiteaConfig { interface GiteaConfig {
protocol?: string; protocol?: string;
@@ -16,7 +16,12 @@ interface Commit {
interface Status { interface Status {
sha: string; sha: string;
state: CiStatus state: CiStatus;
}
interface Hook {
active: boolean;
id: number;
} }
class Gitea { class Gitea {
@@ -95,7 +100,43 @@ class Gitea {
} }
} }
async getHooks(...args: string[]): Promise<Hook[]> {
try {
let headers: HeadersInit = {};
if (this._token) {
headers['Authorization'] = `token ${this._token}`
}
const resp = await ky.get(`${this.getUrlPrefix()}/repos/${args.join('/')}/hooks`, {
headers
});
return await resp.json();
}
catch (err) {
throw err;
}
}
async sendTestWebhook(...args: string[]): Promise<void> {
try {
let headers: HeadersInit = {};
if (this._token) {
headers['Authorization'] = `token ${this._token}`
}
const hook = (await this.getHooks(...args)).find(hook => hook.active === true);
if (!hook) {
throw new Error('No active webhook found');
}
await ky.post(`${this.getUrlPrefix()}/repos/${args.join('/')}/hooks/${hook.id}/tests`, {
headers
});
}
catch (err) {
throw err;
}
}
async waitForBuild(lastHash: string, ...args: string[]): Promise<void> { async waitForBuild(lastHash: string, ...args: string[]): Promise<void> {
let missingStatusCount = 0;
while (true) { while (true) {
let status: Status | null; let status: Status | null;
try { try {
@@ -108,7 +149,15 @@ class Gitea {
await snooze(30000); await snooze(30000);
continue; continue;
} }
if (status.sha !== lastHash) { if (!status.sha && !status.state) {
if (++missingStatusCount > 3) {
console.log('No build info detected. Sending test webhook...');
missingStatusCount = 0;
await this.sendTestWebhook(...args);
}
await snooze(30000);
}
else if (status.sha !== lastHash) {
if (status.state === 'success') { if (status.state === 'success') {
break; break;
} }

View File

@@ -58,10 +58,25 @@ class Pusher {
if (await isPasswordRequired()) { if (await isPasswordRequired()) {
console.log(clc.cyan('Refreshing signature...')); console.log(clc.cyan('Refreshing signature...'));
this._createdSignfile ||= await runCommand('touch', [SIGNFILE]); this._createdSignfile ||= await runCommand('touch', [SIGNFILE]);
if ('SSHKEYSIGN' in process.env)
{
await runCommand('ssh-keygen', [ '-Y', 'sign', '-f', path.resolve(process.env['SSHKEYSIGN'] as string), '-n', ' git', SIGNFILE]);
}
else
{
await runCommand('gpg', ['-a', '--passphrase', escapeCommandParam(this._config.gpgpass || ''), '--batch', '--pinentry-mode', 'loopback', '--detach-sign', SIGNFILE]); await runCommand('gpg', ['-a', '--passphrase', escapeCommandParam(this._config.gpgpass || ''), '--batch', '--pinentry-mode', 'loopback', '--detach-sign', SIGNFILE]);
await fsp.rm(`${SIGNFILE}.asc`); }
if ('SSHKEYSIGN' in process.env)
{
await fsp.rm(`${SIGNFILE}.sig`)
}
else
{
await fsp.rm(`${SIGNFILE}.asc`)
} }
} }
}
increment(pkg: string): Promise<void> { increment(pkg: string): Promise<void> {
return new Promise(async (res, _) => { return new Promise(async (res, _) => {
@@ -133,7 +148,7 @@ class Pusher {
} }
console.log(clc.yellowBright('Running artix-checkupdates')); console.log(clc.yellowBright('Running artix-checkupdates'));
const actionable = job.increment ? job.packages : (await (!!job.source ? checkupdates.fetchMovable() : checkupdates.fetchUpgradable())).map(res => res.basename); const actionable = job.increment ? job.packages : (await (!!job.source ? checkupdates.fetchLooseMovable() : checkupdates.fetchUpgradable())).map(res => res.basename);
// order is IMPORTANT. Must be BLOCKING. // order is IMPORTANT. Must be BLOCKING.
for (let i = 0; i < (job.packages || []).length; i++) { for (let i = 0; i < (job.packages || []).length; i++) {