Skip to content

Automating docker-node part of the official Node Docker images release process #1646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 80 commits into from
Mar 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
d04fe8f
GitHub action and script to retrieve versions
Pehesi97 Feb 18, 2022
93e42d6
Check if there are any new versions
Pehesi97 Feb 22, 2022
47809f1
exit if there is no new version
Pehesi97 Feb 22, 2022
613a705
check for security releases
Pehesi97 Feb 22, 2022
44086cd
remove trailing spaces
Pehesi97 Feb 23, 2022
a6da360
fix comparison login
Pehesi97 Feb 23, 2022
d063f38
musl version and security release logic
Pehesi97 Feb 23, 2022
578e4e0
comment ranUpdates logic
Pehesi97 Feb 23, 2022
8c80519
replace map by for..of
Pehesi97 Feb 23, 2022
926b1a6
for..of newVersions Object.keys
Pehesi97 Feb 23, 2022
4f6c62e
revert logic changes
Pehesi97 Feb 23, 2022
a28cc65
testing the automation in github actions
Pehesi97 Feb 24, 2022
92c29cd
replace source with .
Pehesi97 Feb 24, 2022
c5333a7
set -ue
Pehesi97 Feb 24, 2022
029d93c
use bash as shell
Pehesi97 Feb 24, 2022
56bd0cb
stop using set -ue
Pehesi97 Feb 24, 2022
e823a9d
debug
Pehesi97 Feb 24, 2022
d27ea05
debug
Pehesi97 Feb 27, 2022
7693e81
debug
Pehesi97 Feb 27, 2022
d362256
more debug
Pehesi97 Feb 27, 2022
ebd3737
not running actions on the official repo for now
Pehesi97 Feb 27, 2022
81293c6
test PR creation
Pehesi97 Feb 27, 2022
dd4234a
using main as the base branch for the newly created PR
Pehesi97 Feb 27, 2022
86601f7
fallback value for base branch
Pehesi97 Feb 27, 2022
35aa688
using single quotes for fallback value
Pehesi97 Feb 27, 2022
df65183
github ref name
Pehesi97 Feb 27, 2022
5185650
ref_name typo
Pehesi97 Feb 27, 2022
67137ce
using pull request head ref for testing the action
Pehesi97 Feb 27, 2022
8753805
fix typo
Pehesi97 Feb 27, 2022
ad1eabd
testing check suite pass
Pehesi97 Feb 27, 2022
1cc6a5b
prevent cron from running for now
Pehesi97 Feb 27, 2022
1b5da14
Update .github/workflows/automatic-updates.yml
Pehesi97 Feb 28, 2022
b912711
apply changes asked by SimenB
Pehesi97 Feb 28, 2022
56503a3
Merge branch 'gh-action-automate' of github.com:Pehesi97/docker-node …
Pehesi97 Feb 28, 2022
8e0e021
trigger pr synchronize
Pehesi97 Feb 28, 2022
f21ad78
removes mapSeries
Pehesi97 Feb 28, 2022
18c71d0
for..of with actually iterable object
Pehesi97 Feb 28, 2022
c18b65b
using main branch as base to the generated PR
Pehesi97 Feb 28, 2022
dafff87
head_branch option
Pehesi97 Feb 28, 2022
c0953d9
check_suite event without arguments
Pehesi97 Mar 1, 2022
759a0f4
testing auto-merge
Pehesi97 Mar 2, 2022
2240470
triggering action
Pehesi97 Mar 2, 2022
ee8b9e9
add a simple sleep
Pehesi97 Mar 2, 2022
b2eec25
wait 1 sec
Pehesi97 Mar 2, 2022
1d8bfcd
wait 2 seconds
Pehesi97 Mar 2, 2022
dc117df
wait 10 seconds
Pehesi97 Mar 2, 2022
e35cab7
create update PR
Pehesi97 Mar 2, 2022
06e3eea
changing base branch
Pehesi97 Mar 2, 2022
1550ccb
testing auto-merge in test-branch-main
Pehesi97 Mar 2, 2022
41358c8
checkout again once the PR is created
Pehesi97 Mar 2, 2022
a3a2835
await setTimeout
Pehesi97 Mar 2, 2022
e6566a7
fix arguments
Pehesi97 Mar 2, 2022
3f2a7b9
debug
Pehesi97 Mar 2, 2022
212e6c5
debug
Pehesi97 Mar 2, 2022
94d1081
ignoring update branch
Pehesi97 Mar 2, 2022
eedf0cb
debug schedule
Pehesi97 Mar 2, 2022
88d3e3b
finish
Pehesi97 Mar 2, 2022
01b63ba
remove PR for updates section from CONTRIBUTING.md
Pehesi97 Mar 2, 2022
ff6c3da
remove unused file
Pehesi97 Mar 2, 2022
d2dacf7
Update .github/workflows/automatic-updates.yml
Mar 3, 2022
cc73894
Update .github/workflows/automatic-updates.yml
Mar 3, 2022
0d254b1
requested changes
Pehesi97 Mar 3, 2022
6767207
requested changes pt 2
Pehesi97 Mar 3, 2022
ae95adf
requested changes pt 3
Pehesi97 Mar 6, 2022
4ec8a7f
Update check-pr-status.mjs
Mar 9, 2022
cbeb27b
Update build-automation.mjs
Mar 9, 2022
b6a6b36
Update build-automation.mjs
Mar 9, 2022
2d8fefa
Update build-automation.mjs
Mar 9, 2022
dcb738b
Update build-automation.mjs
Mar 9, 2022
c5df443
requested changes pt 4
Pehesi97 Mar 9, 2022
bc31eb8
chore: move continue statement
SimenB Mar 10, 2022
9b6d362
oops
SimenB Mar 10, 2022
9e8edb9
Update .github/workflows/automatic-updates.yml
Mar 10, 2022
a470faa
auto approve PR when checks pass
Pehesi97 Mar 10, 2022
7b48bda
juliangruber's auto-approve action
Mar 10, 2022
f17b561
Update .github/workflows/automatic-updates.yml
Mar 10, 2022
82f03ea
Update .github/workflows/automatic-updates.yml
Mar 10, 2022
89773e7
Update .github/workflows/automatic-updates.yml
Mar 10, 2022
31e167f
Update .github/workflows/automatic-updates.yml
Mar 10, 2022
efc5719
tweaks
SimenB Mar 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/automatic-updates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Automatically update Docker image versions

on:
schedule:
- cron: "*/15 * * * *"

jobs:
build:
runs-on: ubuntu-latest
if: github.repository_owner == 'nodejs'

steps:
- uses: actions/checkout@v3

- name: Run automation script
uses: actions/github-script@v6
id: updt
with:
script: |
const { default: script } = await import(`${process.env.GITHUB_WORKSPACE}/build-automation.mjs`);
await script(github);

- name: Create update PR
id: cpr
uses: peter-evans/create-pull-request@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: update-branch
base: main
commit-message: "Update to ${{ steps.updt.outputs.updated-versions }}"
title: "Update to ${{ steps.updt.outputs.updated-versions }}"
delete-branch: true
team-reviewers: |
@nodejs/docker

- name: Check CI status periodically
uses: actions/github-script@v6
with:
script: |
const { default: script } = await import(`${process.env.GITHUB_WORKSPACE}/check-pr-status.mjs`);
await script(github, '${{ github.repository }}', ${{ steps.cpr.outputs.pull-request-number }});

- name: Auto-approve the PR
uses: juliangruber/approve-pull-request-action@v1
with:
# Cannot use `GITHUB_TOKEN` as it's not allowed to approve own PR
github-token: ${{ secrets.GH_API_TOKEN }}
number: ${{ steps.cpr.outputs.pull-request-number }}

- name: Merge PR
uses: juliangruber/merge-pull-request-action@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ steps.cpr.outputs.pull-request-number }}
104 changes: 104 additions & 0 deletions build-automation.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { promisify } from "util";

import child_process from "child_process";

const exec = promisify(child_process.exec);

// a function that queries the Node.js release website for new versions,
// compare the available ones with the ones we use in this repo
// and returns whether we should update or not
const checkIfThereAreNewVersions = async (github) => {
try {
const { stdout: versionsOutput } = await exec(". ./functions.sh && get_versions", { shell: "bash" });

const supportedVersions = versionsOutput.trim().split(" ");

let latestSupportedVersions = {};

for (let supportedVersion of supportedVersions) {
const { stdout } = await exec(`ls ${supportedVersion}`);

const { stdout: fullVersionOutput } = await exec(`. ./functions.sh && get_full_version ./${supportedVersion}/${stdout.trim().split("\n")[0]}`, { shell: "bash" });

console.log(fullVersionOutput);

latestSupportedVersions[supportedVersion] = { fullVersion: fullVersionOutput.trim() };
}

const { data: availableVersionsJson } = await github.request('https://nodejs.org/download/release/index.json');

// filter only more recent versions of availableVersionsJson for each major version in latestSupportedVersions' keys
// e.g. if latestSupportedVersions = { "12": "12.22.10", "14": "14.19.0", "16": "16.14.0", "17": "17.5.0" }
// and availableVersions = ["Node.js 12.22.10", "Node.js 12.24.0", "Node.js 14.19.0", "Node.js 14.22.0", "Node.js 16.14.0", "Node.js 16.16.0", "Node.js 17.5.0", "Node.js 17.8.0"]
// return { "12": "12.24.0", "14": "14.22.0", "16": "16.16.0", "17": "17.8.0" }

let filteredNewerVersions = {};

for (let availableVersion of availableVersionsJson) {
const [availableMajor, availableMinor, availablePatch] = availableVersion.version.split("v")[1].split(".");
if (latestSupportedVersions[availableMajor] == null) {
continue;
}
const [_latestMajor, latestMinor, latestPatch] = latestSupportedVersions[availableMajor].fullVersion.split(".");
if (latestSupportedVersions[availableMajor] && (Number(availableMinor) > Number(latestMinor) || (availableMinor === latestMinor && Number(availablePatch) > Number(latestPatch)))) {
filteredNewerVersions[availableMajor] = { fullVersion: `${availableMajor}.${availableMinor}.${availablePatch}` };
}
}

return {
shouldUpdate: Object.keys(filteredNewerVersions).length > 0 && JSON.stringify(filteredNewerVersions) !== JSON.stringify(latestSupportedVersions),
versions: filteredNewerVersions,
}
} catch (error) {
console.error(error);
process.exit(1);
}
};

// a function that queries the Node.js unofficial release website for new musl versions and security releases,
// and returns relevant information
const checkForMuslVersionsAndSecurityReleases = async (github, versions) => {
try {
const { data: unofficialBuildsIndexText } = await github.request('https://unofficial-builds.nodejs.org/download/release/index.json');

for (let version of Object.keys(versions)) {
const { data: unofficialBuildsWebsiteText } = await github.request(`https://unofficial-builds.nodejs.org/download/release/v${versions[version].fullVersion}`);

versions[version].muslBuildExists = unofficialBuildsWebsiteText.includes("musl");
versions[version].isSecurityRelease = unofficialBuildsIndexText.find(indexVersion => indexVersion.version === `v${versions[version].fullVersion}`)?.security;
}
return versions;
} catch (error) {
console.error(error);
process.exit(1);
}
};

export default async function(github) {
// if there are no new versions, exit gracefully
// if there are new versions,
// check for musl builds
// then run update.sh
const { shouldUpdate, versions } = await checkIfThereAreNewVersions(github);

if (!shouldUpdate) {
console.log("No new versions found. No update required.");
process.exit(0);
} else {
const newVersions = await checkForMuslVersionsAndSecurityReleases(github, versions);
let updatedVersions = [];
for (let version of Object.keys(newVersions)) {
if (newVersions[version].muslBuildExists) {
const { stdout } = await exec(`./update.sh ${newVersions[version].isSecurityRelease ? "-s " : ""}${version}`);
console.log(stdout);
updatedVersions.push(newVersions[version].fullVersion);
} else {
console.log(`There's no musl build for version ${newVersions[version].fullVersion} yet.`);
process.exit(0);
}
}
console.log(`::set-output name=updated-versions::${updatedVersions.join(',')}`);
const { stdout } = (await exec(`git diff`));
console.log(stdout);
}
}
29 changes: 29 additions & 0 deletions check-pr-status.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// fetch /repos/{owner}/{repo}/pulls/{pull_number}
// and check its mergeable_state
// if "clean", exit with status code 0
// else exit with error
import { setTimeout } from 'timers/promises';

const tries = 10;
const retryDelay = 30000;

export default async function(github, repository, pull_number) {
const [owner, repo] = repository.split('/');
await setTimeout(retryDelay);

for (let t = 0; t < tries; t++) {
try {
const { data } = await github.rest.pulls.get({owner, repo, pull_number})

console.log(data);
if (data.mergeable_state === 'clean') {
process.exit(0);
}
await setTimeout(retryDelay);
} catch (error) {
console.error(error);
process.exit(1);
}
}
process.exit(1);
}
2 changes: 2 additions & 0 deletions functions.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env bash
#
# Utlity functions
# Don't change this file unless needed
# The GitHub Action for automating new builds rely on this file

info() {
printf "%s\\n" "$@"
Expand Down