-
Couldn't load subscription status.
- Fork 67
Detect ubuntu version from /etc/os-release if ImageOS = Linux #327
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer to keep this in a single file, for now. If the time comes to split we'll do a separate pull request. (also, this way we have to compare/review across different files, which is not ideal) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Feel free to follow up with a refactor update, if you'd like, but lets keep concerns separated. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| const fs = require('node:fs') | ||
| const core = require('@actions/core') | ||
|
|
||
| const IMAGE_OS_TO_RUNNER_OS = { | ||
| ubuntu18: 'ubuntu-18.04', | ||
| ubuntu20: 'ubuntu-20.04', | ||
| ubuntu22: 'ubuntu-22.04', | ||
| ubuntu24: 'ubuntu-24.04', | ||
| win19: 'windows-2019', | ||
| win22: 'windows-2022', | ||
| } | ||
|
|
||
| function getRunnerOSVersion(imageOS, readFile = fs.readFileSync) { | ||
| core.startGroup('Get runner OS version') | ||
|
|
||
| core.info(`ImageOS is set to '${imageOS}'`) | ||
|
|
||
| if (imageOS === 'Linux') { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be better to just look at |
||
| core.info('Attempting to detect OS version from /etc/os-release.') | ||
|
|
||
| try { | ||
| const osRelease = parseOSRelease(readFile) | ||
| core.debug('Parsed /etc/os-release: ' + JSON.stringify(osRelease)) | ||
|
|
||
| const shortVersion = osRelease.VERSION_ID?.split('.')[0] | ||
| imageOS = osRelease.ID + shortVersion | ||
|
|
||
| core.info(`Detected '${imageOS}'`) | ||
| } catch (error) { | ||
| core.error('Failed to parse /etc/os-release: ' + error.message) | ||
| } | ||
| } | ||
|
|
||
| const containerFromEnvImageOS = IMAGE_OS_TO_RUNNER_OS[imageOS] | ||
| if (!containerFromEnvImageOS) { | ||
| throw new Error( | ||
| "Tried to map a target OS from env. variable 'ImageOS' (got " + | ||
| `${imageOS}` + | ||
| "), but failed. If you're using a " + | ||
| "self-hosted runner, you should set 'env': 'ImageOS': ... to one of the following: " + | ||
| "['" + | ||
| `${Object.keys(IMAGE_OS_TO_RUNNER_OS).join("', '")}` + | ||
| "']", | ||
| ) | ||
| } | ||
|
|
||
| core.info('Resulting runner OS: ' + containerFromEnvImageOS) | ||
|
|
||
| core.endGroup() | ||
|
|
||
| return containerFromEnvImageOS | ||
| } | ||
|
|
||
| /** | ||
| * Returns an Object with the contents of `/etc/os-release`, with the keys | ||
| * being the left side of the '=' and the values being the right side. | ||
| * The values are stripped of quotes and whitespace. | ||
| * | ||
| * Usually, this should result in an object like: | ||
| * | ||
| * { | ||
| * ID: 'ubuntu', | ||
| * VERSION_ID: '24.04', | ||
| * // ...other keys | ||
| * } | ||
| * | ||
| * @returns {Object} The contents of `/etc/os-release` as an object. | ||
| */ | ||
| function parseOSRelease(readFile) { | ||
| const osRelease = readFile('/etc/os-release', 'utf8') | ||
| const lines = osRelease.split('\n') | ||
|
|
||
| return Object.fromEntries( | ||
| lines | ||
| .filter((line) => line.includes('=')) | ||
| .map((line) => { | ||
| const [key, value] = line.split('=') | ||
| return [key.trim(), value.replace(/"/g, '').trim()] | ||
| }), | ||
| ) | ||
| } | ||
|
|
||
| module.exports = { | ||
| getRunnerOSVersion, | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think you ran |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,8 @@ const semver = require('semver') | |
| const fs = require('fs') | ||
| const os = require('os') | ||
|
|
||
| const { getRunnerOSVersion } = require('./lib/getRunnerOSVersion') | ||
|
|
||
| const MAX_HTTP_RETRIES = 3 | ||
|
|
||
| main().catch((err) => { | ||
|
|
@@ -27,7 +29,7 @@ async function main() { | |
| versions = parseVersionFile(versionFilePath) | ||
| } | ||
|
|
||
| const osVersion = getRunnerOSVersion() | ||
| const osVersion = getRunnerOSVersion(process.env.ImageOS) | ||
|
Comment on lines
-30
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer to keep the |
||
| const otpSpec = getInput('otp-version', true, 'erlang', versions) | ||
| const elixirSpec = getInput('elixir-version', false, 'elixir', versions) | ||
| const gleamSpec = getInput('gleam-version', false, 'gleam', versions) | ||
|
|
@@ -538,31 +540,6 @@ function getRunnerOSArchitecture() { | |
| ) | ||
| } | ||
|
|
||
| function getRunnerOSVersion() { | ||
| const ImageOSToContainer = { | ||
| ubuntu18: 'ubuntu-18.04', | ||
| ubuntu20: 'ubuntu-20.04', | ||
| ubuntu22: 'ubuntu-22.04', | ||
| ubuntu24: 'ubuntu-24.04', | ||
| win19: 'windows-2019', | ||
| win22: 'windows-2022', | ||
| } | ||
| const containerFromEnvImageOS = ImageOSToContainer[process.env.ImageOS] | ||
| if (!containerFromEnvImageOS) { | ||
| throw new Error( | ||
| "Tried to map a target OS from env. variable 'ImageOS' (got " + | ||
| `${process.env.ImageOS}` + | ||
| "), but failed. If you're using a " + | ||
| "self-hosted runner, you should set 'env': 'ImageOS': ... to one of the following: " + | ||
| "['" + | ||
| `${Object.keys(ImageOSToContainer).join("', '")}` + | ||
| "']", | ||
| ) | ||
| } | ||
|
|
||
| return containerFromEnvImageOS | ||
| } | ||
|
|
||
| async function getUrlResponse(url, headers, attempt = 1) { | ||
| try { | ||
| const response = await fetch(url, { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| NAME="Alpine Linux" | ||
| ID=alpine | ||
| VERSION_ID=3.21.3 | ||
| PRETTY_NAME="Alpine Linux v3.21" | ||
| HOME_URL="https://alpinelinux.org/" | ||
| BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| PRETTY_NAME="Ubuntu 22.04.5 LTS" | ||
| NAME="Ubuntu" | ||
| VERSION_ID="22.04" | ||
| VERSION="22.04.5 LTS (Jammy Jellyfish)" | ||
| VERSION_CODENAME=jammy | ||
| ID=ubuntu | ||
| ID_LIKE=debian | ||
| HOME_URL="https://www.ubuntu.com/" | ||
| SUPPORT_URL="https://help.ubuntu.com/" | ||
| BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" | ||
| PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" | ||
| UBUNTU_CODENAME=jammy |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| PRETTY_NAME="Ubuntu 24.04.2 LTS" | ||
| NAME="Ubuntu" | ||
| VERSION_ID="24.04" | ||
| VERSION="24.04.2 LTS (Noble Numbat)" | ||
| VERSION_CODENAME=noble | ||
| ID=ubuntu | ||
| ID_LIKE=debian | ||
| HOME_URL="https://www.ubuntu.com/" | ||
| SUPPORT_URL="https://help.ubuntu.com/" | ||
| BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" | ||
| PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" | ||
| UBUNTU_CODENAME=noble | ||
| LOGO=ubuntu-logo |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| PRETTY_NAME="Ubuntu 25.04" | ||
| NAME="Ubuntu" | ||
| VERSION_ID="25.04" | ||
| VERSION="25.04 (Plucky Puffin)" | ||
| VERSION_CODENAME=plucky | ||
| ID=ubuntu | ||
| ID_LIKE=debian | ||
| HOME_URL="https://www.ubuntu.com/" | ||
| SUPPORT_URL="https://help.ubuntu.com/" | ||
| BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" | ||
| PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" | ||
| UBUNTU_CODENAME=plucky | ||
| LOGO=ubuntu-logo |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| const { describe, it, afterEach } = require('node:test') | ||
| const assert = require('node:assert') | ||
| const { readFileSync } = require('node:fs') | ||
| const { join } = require('node:path') | ||
|
|
||
| const { getRunnerOSVersion } = require('../src/lib/getRunnerOSVersion') | ||
|
|
||
| describe('getRunnerOSVersion', () => { | ||
| it('throws if no ImageOS env exists', async () => { | ||
paulo-ferraz-oliveira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| assert.throws(() => { | ||
| getRunnerOSVersion(undefined) | ||
| }) | ||
| }) | ||
|
|
||
| describe('simple mappings', () => { | ||
| for (const [imageOS, runnerOS] of [ | ||
| ['ubuntu18', 'ubuntu-18.04'], | ||
| ['ubuntu20', 'ubuntu-20.04'], | ||
| ['ubuntu22', 'ubuntu-22.04'], | ||
| ['ubuntu24', 'ubuntu-24.04'], | ||
| ['win19', 'windows-2019'], | ||
| ['win22', 'windows-2022'], | ||
| ]) { | ||
| it(`returns ${runnerOS} for ${imageOS}`, () => { | ||
| const result = getRunnerOSVersion(imageOS) | ||
|
|
||
| assert.strictEqual(result, runnerOS) | ||
| }) | ||
| } | ||
| }) | ||
|
|
||
| describe('detect ubuntu version if "ImageOS" is "Linux"', () => { | ||
| afterEach(() => { | ||
| // Clear the mock filesystem after each test. | ||
| for (const path in mockFS) { | ||
| delete mockFS[path] | ||
| } | ||
| }) | ||
|
|
||
| it('detects ubuntu 24.04', () => { | ||
| mockFS['/etc/os-release'] = loadFixture('ubuntu24.txt') | ||
|
|
||
| const result = getRunnerOSVersion('Linux', mockedReadFileSync) | ||
| assert.strictEqual(result, 'ubuntu-24.04') | ||
| }) | ||
|
|
||
| it('detects ubuntu 22.04', () => { | ||
| mockFS['/etc/os-release'] = loadFixture('ubuntu22.txt') | ||
|
|
||
| const result = getRunnerOSVersion('Linux', mockedReadFileSync) | ||
| assert.strictEqual(result, 'ubuntu-22.04') | ||
| }) | ||
|
|
||
| it('rejects ubuntu 25.04 (valid format, but not in availability table)', () => { | ||
| mockFS['/etc/os-release'] = loadFixture('ubuntu25.txt') | ||
|
|
||
| assert.throws(() => { | ||
| getRunnerOSVersion('Linux', mockedReadFileSync) | ||
| }) | ||
| }) | ||
|
|
||
| it('rejects alpine 3 (invalid distro)', () => { | ||
| mockFS['/etc/os-release'] = loadFixture('alpine3.txt') | ||
|
|
||
| assert.throws(() => { | ||
| getRunnerOSVersion('Linux', mockedReadFileSync) | ||
| }) | ||
| }) | ||
|
|
||
| it('fails with usual message if /etc/os-release is not found', () => { | ||
| assert.throws( | ||
| () => { | ||
| getRunnerOSVersion('Linux', mockedReadFileSync) | ||
| }, | ||
| { | ||
| message: /(got Linux)/, | ||
| }, | ||
| ) | ||
| }) | ||
| }) | ||
| }) | ||
|
|
||
| function loadFixture(filename) { | ||
| const path = join(__dirname, 'fixtures', 'os-release', filename) | ||
|
|
||
| return readFileSync(path, 'utf8') | ||
| } | ||
|
|
||
| const mockFS = {} | ||
|
|
||
| function mockedReadFileSync(path) { | ||
| if (Object.hasOwn(mockFS, path)) { | ||
| return mockFS[path] | ||
| } | ||
|
|
||
| throw new Error(`File not found in mockFS: ${path} (forgot to mock it?)`) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.