From 0198fc53d94ca5c566ec95fc5342822ec6bb466d Mon Sep 17 00:00:00 2001 From: Marc Jakobi Date: Sun, 19 Mar 2023 14:35:02 +0100 Subject: [PATCH] chore: split script up into github-action and luarocks-tag-release --- action.yml | 40 ++-- bin/luarocks-tag-release-action.lua | 110 +++++++++++ bin/luarocks-tag-release.lua | 282 ---------------------------- entrypoint.sh | 5 - flake.nix | 12 +- lua/luarocks-tag-release.lua | 193 +++++++++++++++++++ 6 files changed, 328 insertions(+), 314 deletions(-) create mode 100644 bin/luarocks-tag-release-action.lua delete mode 100755 bin/luarocks-tag-release.lua delete mode 100755 entrypoint.sh create mode 100755 lua/luarocks-tag-release.lua diff --git a/action.yml b/action.yml index 8ec9cc0..83d3a94 100644 --- a/action.yml +++ b/action.yml @@ -16,12 +16,10 @@ inputs: default: ${{ github.ref_name }} dependencies: description: "List of LuaRocks dependencies." - required: true - default: "" + required: false labels: description: "List of package labels." - required: true - default: "" + required: false copy_directories: description: | List of additional directories to copy. @@ -33,12 +31,10 @@ inputs: {{ neovim.plugin.dirs }} summary: description: "Short description of the package." - required: true - default: "" + required: false detailed_description: description: "Detailed description of the package." - required: true - default: "" + required: false build_type: description: "The build type." required: true @@ -70,17 +66,19 @@ runs: - uses: cachix/install-nix-action@v20 - run: echo "${{ github.action_path }}" >> $GITHUB_PATH shell: bash - - run: | - entrypoint.sh - \ ${{ inputs.name }} - \ ${{ inputs.version }} - \ ${{ inputs.dependencies }} - \ ${{ inputs.labels }} - \ ${{ inputs.copy-directories }} - \ ${{ inputs.summary }} - \ ${{ inputs.detailed_description }} - \ ${{ inputs.build_type }} - \ ${{ inputs.template }} - \ ${{ inputs.upload }} - \ ${{ inputs.license }} + - run: nix profile install ".#luarocks-tag-release-action" + shell: bash + - run: luarocks-tag-release-action + env: + INPUT_NAME: ${{ inputs.name }} + INPUT_VERSION: ${{ inputs.version }} + INPUT_DEPENDENCIES: ${{ inputs.dependencies }} + INPUT_LABELS: ${{ inputs.labels }} + INPUT_COPY_DIRECTORIES: ${{ inputs.copy-directories }} + INPUT_SUMMARY: ${{ inputs.summary }} + INPUT_DETAILED_DESCRIPTION: ${{ inputs.detailed_description }} + INPUT_BUILD_TYPE: ${{ inputs.build_type }} + INPUT_TEMPLATE: ${{ inputs.template }} + INPUT_UPLOAD: ${{ inputs.upload }} + INPUT_LICENSE: ${{ inputs.license }} shell: bash diff --git a/bin/luarocks-tag-release-action.lua b/bin/luarocks-tag-release-action.lua new file mode 100644 index 0000000..d7c4fad --- /dev/null +++ b/bin/luarocks-tag-release-action.lua @@ -0,0 +1,110 @@ +assert(os.getenv('LUAROCKS_API_KEY'), 'LUAROCKS_API_KEY secret not set') + +local function getenv_or_err(env_var) + return assert(os.getenv(env_var), env_var .. ' not set.') +end + +local function getenv_or_empty(env_var) + return os.getenv(env_var) or '' +end + +local github_repo = getenv_or_err('GITHUB_REPOSITORY') + +local repo_name = assert( + string.match(github_repo, '/(.+)'), + [[ + Could not determine repository name from GITHUB_REPOSITORY. + If you see this, please report this as a bug. + ]] +) + +local github_server_url = getenv_or_err('GITHUB_SERVER_URL') + +---@param str string +---@return string[] list_arg +local function parse_list_args(str) + local tbl = {} + for arg in string.gmatch(str, '[^\r\n]+') do + table.insert(tbl, arg) + end + return tbl +end + +---Filter out directories that don't exist. +---@param directories string[] List of directories. +---@return string[] existing_directories +local function filter_existing_directories(directories) + local existing_directories = {} + for _, dir in pairs(directories) do + if require('lfs').attributes(dir, 'mode') == 'directory' then + existing_directories[#existing_directories + 1] = dir + end + end + return existing_directories +end + +---Insert Neovim plugin directories into the `copy_directories` list +---@param copy_directories string[] List of directories +local function insert_neovim_plugin_dirs(copy_directories) + local neovim_plugin_dirs = { + 'autoload', + 'colors', + 'compiler', + 'doc', + 'filetype.lua', + 'indent', + 'keymap', + 'lang', + 'menu.vim', + 'parser', + 'plugin', + 'queries', + 'query', + 'rplugin', + 'spell', + 'syntax', + } + for _, dir in neovim_plugin_dirs do + table.insert(copy_directories, dir) + end +end + +---@param str_args string The arguments to parse +---@return string[] copy_directories The directories to copy +local function parse_copy_directory_args(str_args) + local args = parse_list_args(str_args) + local copy_directories = {} + for _, arg in pairs(args) do + if string.match(arg, '{{ neovim.plugin.dirs }}') then + insert_neovim_plugin_dirs(copy_directories) + else + copy_directories[#copy_directories + 1] = arg + end + end + return filter_existing_directories(copy_directories) +end + +local license_input = os.getenv('INPUT_LICENSE') + +---@type Args +local args = { + github_repo = github_repo, + repo_name = repo_name, + github_server_url = github_server_url, + package_name = getenv_or_err('INPUT_NAME'), + package_version = getenv_or_err('INPUT_VERSION'), + dependencies = parse_list_args(getenv_or_empty('INPUT_DEPENDENCIES')), + labels = parse_list_args(getenv_or_empty('INPUT_LABELS')), + copy_directories = parse_copy_directory_args(getenv_or_err('INPUT_COPY_DIRECTORIES')), + summary = getenv_or_empty('INPUT_SUMMARY'), + detailed_description_lines = parse_list_args(getenv_or_empty('INPUT_DETAILED_DESCRIPTION')), + build_type = getenv_or_err('INPUT_BUILD_TYPE'), + rockspec_template_file_path = getenv_or_err('INPUT_TEMPLATE'), + upload = getenv_or_err('INPUT_UPLOAD') == 'true', + license = license_input ~= '' and license_input or nil, +} +table.insert(args.dependencies, 1, 'lua >= 5.1') + +local luarocks_tag_release = require('luarocks-tag-release') + +luarocks_tag_release(args) diff --git a/bin/luarocks-tag-release.lua b/bin/luarocks-tag-release.lua deleted file mode 100755 index dec8f37..0000000 --- a/bin/luarocks-tag-release.lua +++ /dev/null @@ -1,282 +0,0 @@ -#!/usr/bin/env lua - ----@class Args ----@field package_name string ----@field package_version string ----@field dependencies string[] ----@field labels string[] ----@field copy_directories string[] ----@field summary string ----@field detailed_description_lines string[] ----@field build_type string ----@field rockspec_template_file_path string ----@field upload boolean ----@field license string|nil - ----@type string[] -local arg_list = { ... } - -assert(os.getenv('LUAROCKS_API_KEY'), 'LUAROCKS_API_KEY secret not set') - -local github_repo = assert(os.getenv('GITHUB_REPOSITORY'), 'GITHUB_REPOSITORY not set') - -local repo_name = assert( - string.match(github_repo, '/(.+)'), - [[ - Could not determine repository name from GITHUB_REPOSITORY. - If you see this, please report this as a bug. - ]] -) - -local github_server_url = assert(os.getenv('GITHUB_SERVER_URL'), 'GITHUB_SERVER_URL not set') - ----@param str string ----@return string[] list_arg -local function parse_list_args(str) - local tbl = {} - for arg in string.gmatch(str, '[^\r\n]+') do - table.insert(tbl, arg) - end - return tbl -end - ----Filter out directories that don't exist. ----@param directories string[] List of directories. ----@return string[] existing_directories -local function filter_existing_directories(directories) - local existing_directories = {} - for _, dir in pairs(directories) do - if require('lfs').attributes(dir, 'mode') == 'directory' then - existing_directories[#existing_directories + 1] = dir - end - end - return existing_directories -end - ----Insert Neovim plugin directories into the `copy_directories` list ----@param copy_directories string[] List of directories -local function insert_neovim_plugin_dirs(copy_directories) - local neovim_plugin_dirs = { - 'autoload', - 'colors', - 'compiler', - 'doc', - 'filetype.lua', - 'indent', - 'keymap', - 'lang', - 'menu.vim', - 'parser', - 'plugin', - 'queries', - 'query', - 'rplugin', - 'spell', - 'syntax', - } - for _, dir in neovim_plugin_dirs do - table.insert(copy_directories, dir) - end -end - ----@param str_args string The arguments to parse ----@return string[] copy_directories The directories to copy -local function parse_copy_directory_args(str_args) - local args = parse_list_args(str_args) - local copy_directories = {} - for _, arg in pairs(args) do - if string.match(arg, '{{ neovim.plugin.dirs }}') then - insert_neovim_plugin_dirs(copy_directories) - else - copy_directories[#copy_directories + 1] = arg - end - end - return filter_existing_directories(copy_directories) -end - ----@type Args -local args = { - package_name = arg_list[1], - package_version = arg_list[2], - dependencies = parse_list_args(arg_list[3]), - labels = parse_list_args(arg_list[4]), - copy_directories = parse_copy_directory_args(arg_list[5]), - summary = arg_list[6], - detailed_description_lines = parse_list_args(arg_list[7]), - build_type = arg_list[8], - rockspec_template_file_path = arg_list[9], - upload = arg_list[10] == 'true', - license = #arg_list > 10 and arg_list[11] ~= '' and arg_list[11] or nil, -} -table.insert(args.dependencies, 1, 'lua >= 5.1') - -local modrev = string.gsub(args.package_version, 'v', '') - -local is_tag = os.getenv('GITHUB_REF_TYPE') == 'tag' -local git_ref = assert(os.getenv('GITHUB_REF_NAME'), 'GITHUB_REF_NAME not set') -local archive_dir_suffix = modrev -if not is_tag then - print('Publishing an untagged release.') - git_ref = assert(os.getenv('GITHUB_SHA'), 'GITHUB_SHA not set') - archive_dir_suffix = git_ref -end - -local target_rockspec_file = args.package_name .. '-' .. modrev .. '-1.rockspec' - ----@param filename string ----@return string? content -local function read_file(filename) - local content - local f = io.open(filename, 'r') - if f then - content = f:read('*a') - f:close() - end - return content -end - ----@param cmd string ----@param on_failure fun(error_msg:string) ----@return string stdout, string stderr -local function execute(cmd, on_failure) - local exec_out = 'exec_out.txt' - local exec_err = 'exec_err.txt' - local to_exec_out = ' >' .. exec_out .. ' 2>' .. exec_err - local exit_code = os.execute(cmd .. to_exec_out) - local stdout = read_file(exec_out) or '' - local stderr = read_file(exec_err) or '' - if exit_code ~= 0 then - on_failure('FAILED (exit code ' .. exit_code .. '): ' .. cmd .. ' ' .. stderr) - end - return stdout, stderr -end - ----@param rockspec_content string ----@return nil -local function luarocks_upload(rockspec_content) - local outfile = assert(io.open(target_rockspec_file, 'w'), 'Could not create ' .. target_rockspec_file .. '.') - outfile:write(rockspec_content) - outfile:close() - local tmp_dir = execute('mktemp -d', error):gsub('\n', '') - local luarocks_install_cmd = 'luarocks install --tree ' .. tmp_dir - - local cmd = luarocks_install_cmd .. ' ' .. target_rockspec_file - print('TEST: ' .. cmd) - local stdout, _ = execute(cmd, error) - print(stdout) - cmd = 'luarocks remove --tree ' .. tmp_dir .. ' ' .. args.package_name - print('TEST: ' .. cmd) - stdout, _ = execute(cmd, error) - if not args.upload then - print('LuaRocks upload disabled. Skipping...') - return - end - print(stdout) - cmd = 'luarocks upload ' .. target_rockspec_file .. ' --api-key $LUAROCKS_API_KEY' - print('UPLOAD: ' .. cmd) - stdout, _ = execute(cmd, error) - print(stdout) - cmd = luarocks_install_cmd .. ' ' .. args.package_name .. ' ' .. modrev - print('TEST: ' .. cmd) - stdout, _ = execute(cmd, print) - print(stdout) -end - ----@param xs string[]? ----@return string lua_list_string -local function mk_lua_list_string(xs) - if not xs or #xs == 0 then - return '{ }' - end - return "{ '" .. table.concat(xs, "', '") .. "' } " -end - ----@param xs string[]? ----@return string lua_multiline_string -local function mk_lua_multiline_str(xs) - if not xs or #xs == 0 then - return "''" - end - return '[[\n ' .. table.concat(xs, '\n ') .. ' \n]]' -end - -print('Using template: ' .. args.rockspec_template_file_path) -local rockspec_template_file = - assert(io.open(args.rockspec_template_file_path, 'r'), 'Could not open ' .. args.rockspec_template_file_path) -local content = rockspec_template_file:read('*a') -rockspec_template_file:close() -local repo_url = github_server_url .. '/' .. github_repo -local homepage = repo_url -local license -local repo_info_str, _ = - execute('curl -H "Accept: application/vnd.github+json" https://api.github.com/repos/' .. github_repo, print) -if repo_info_str and repo_info_str ~= '' then - local json = require('dkjson') - local repo_meta = json.decode(repo_info_str) - local repo_license = repo_meta.license or repo_meta.source and repo_meta.source.license - if args.license then - license = "license = '" .. args.license .. "'" - elseif repo_license and repo_license.spdx_id ~= '' and repo_license.spdx_id ~= 'NOASSERTION' then - license = "license = '" .. repo_license.spdx_id .. "'" - else - error([[ - Could not get the license SPDX ID from the GitHub API. - Please add a license file that GitHub can recognise or add a `license` input. - See: https://github.com/nvim-neorocks/luarocks-tag-release#license - ]]) - end - if not args.summary or args.summary == '' then - args.summary = repo_meta.description and repo_meta.description or '' - end - if not args.labels or #args.labels == 0 then - args.labels = repo_meta.topics and repo_meta.topics or {} - end - if repo_meta.homepage and repo_meta.homepage ~= '' then - homepage = repo_meta.homepage - end -end - ----@param str string ----@return string -local function escape_quotes(str) - local escaped = str:gsub("'", "\\'") - return escaped -end - -print( - 'Generating Luarocks release ' - .. modrev - .. ' for: ' - .. args.package_name - .. ' version ' - .. args.package_version - .. ' from ref ' - .. git_ref - .. '.' -) -local rockspec = content - :gsub('$git_ref', git_ref) - :gsub('$modrev', modrev) - :gsub('$repo_url', repo_url) - :gsub('$archive_dir_suffix', archive_dir_suffix) - :gsub('$package', args.package_name) - :gsub('$summary', escape_quotes(args.summary)) - :gsub('$detailed_description', mk_lua_multiline_str(args.detailed_description_lines)) - :gsub('$dependencies', mk_lua_list_string(args.dependencies)) - :gsub('$labels', mk_lua_list_string(args.labels)) - :gsub('$homepage', homepage) - :gsub('$license', license) - :gsub('$copy_directories', mk_lua_list_string(args.copy_directories)) - :gsub('$build_type', args.build_type) - :gsub('$repo_name', repo_name) - -print('') -print('Generated rockspec:') -print('========================================================================================') -print(rockspec) -print('========================================================================================') - -luarocks_upload(rockspec) - -print('') -print('Done.') diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100755 index 7db643a..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -nix build ".#luarocks-tag-release" - -./result/bin/luarocks-tag-release "$@" diff --git a/flake.nix b/flake.nix index 3b846a4..808f4f5 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ perSystem = nixpkgs.lib.genAttrs supportedSystems; pkgsFor = system: import nixpkgs {inherit system;}; - luarocks-tag-release-for = system: let + luarocks-tag-release-action-for = system: let pkgs = pkgsFor system; luarocks-tag-release-wrapped = pkgs.lua51Packages.buildLuaApplication { pname = "luarocks-tag-release"; @@ -37,7 +37,7 @@ }; in pkgs.writeShellApplication { - name = "luarocks-tag-release"; + name = "luarocks-tag-release-action"; runtimeInputs = with pkgs; [ curl gnumake @@ -49,7 +49,7 @@ ]; text = '' - luarocks-tag-release.lua "$@" + luarocks-tag-release-action.lua "$@" ''; # The default checkPhase depends on ShellCheck, which depends on GHC @@ -57,10 +57,10 @@ }; in { packages = perSystem (system: let - luarocks-tag-release = luarocks-tag-release-for system; + luarocks-tag-release-action = luarocks-tag-release-action-for system; in { - default = luarocks-tag-release; - inherit luarocks-tag-release; + default = luarocks-tag-release-action; + inherit luarocks-tag-release-action; }); }; } diff --git a/lua/luarocks-tag-release.lua b/lua/luarocks-tag-release.lua new file mode 100755 index 0000000..6a9d97e --- /dev/null +++ b/lua/luarocks-tag-release.lua @@ -0,0 +1,193 @@ +#!/usr/bin/env lua + +---@class Args +---@field repo_name string +---@field github_repo string +---@field github_server_url string +---@field package_name string +---@field package_version string +---@field dependencies string[] +---@field labels string[] +---@field copy_directories string[] +---@field summary string +---@field detailed_description_lines string[] +---@field build_type string +---@field rockspec_template_file_path string +---@field upload boolean +---@field license string|nil + +---@param args Args +local function luarocks_tag_release(args) + local modrev = string.gsub(args.package_version, 'v', '') + + local is_tag = os.getenv('GITHUB_REF_TYPE') == 'tag' + local git_ref = assert(os.getenv('GITHUB_REF_NAME'), 'GITHUB_REF_NAME not set') + local archive_dir_suffix = modrev + if not is_tag then + print('Publishing an untagged release.') + git_ref = assert(os.getenv('GITHUB_SHA'), 'GITHUB_SHA not set') + archive_dir_suffix = git_ref + end + + local target_rockspec_file = args.package_name .. '-' .. modrev .. '-1.rockspec' + + ---@param filename string + ---@return string? content + local function read_file(filename) + local content + local f = io.open(filename, 'r') + if f then + content = f:read('*a') + f:close() + end + return content + end + + ---@param cmd string + ---@param on_failure fun(error_msg:string) + ---@return string stdout, string stderr + local function execute(cmd, on_failure) + local exec_out = 'exec_out.txt' + local exec_err = 'exec_err.txt' + local to_exec_out = ' >' .. exec_out .. ' 2>' .. exec_err + local exit_code = os.execute(cmd .. to_exec_out) + local stdout = read_file(exec_out) or '' + local stderr = read_file(exec_err) or '' + if exit_code ~= 0 then + on_failure('FAILED (exit code ' .. exit_code .. '): ' .. cmd .. ' ' .. stderr) + end + return stdout, stderr + end + + ---@param rockspec_content string + ---@return nil + local function luarocks_upload(rockspec_content) + local outfile = assert(io.open(target_rockspec_file, 'w'), 'Could not create ' .. target_rockspec_file .. '.') + outfile:write(rockspec_content) + outfile:close() + local tmp_dir = execute('mktemp -d', error):gsub('\n', '') + local luarocks_install_cmd = 'luarocks install --tree ' .. tmp_dir + + local cmd = luarocks_install_cmd .. ' ' .. target_rockspec_file + print('TEST: ' .. cmd) + local stdout, _ = execute(cmd, error) + print(stdout) + cmd = 'luarocks remove --tree ' .. tmp_dir .. ' ' .. args.package_name + print('TEST: ' .. cmd) + stdout, _ = execute(cmd, error) + if not args.upload then + print('LuaRocks upload disabled. Skipping...') + return + end + print(stdout) + cmd = 'luarocks upload ' .. target_rockspec_file .. ' --api-key $LUAROCKS_API_KEY' + print('UPLOAD: ' .. cmd) + stdout, _ = execute(cmd, error) + print(stdout) + cmd = luarocks_install_cmd .. ' ' .. args.package_name .. ' ' .. modrev + print('TEST: ' .. cmd) + stdout, _ = execute(cmd, print) + print(stdout) + end + + ---@param xs string[]? + ---@return string lua_list_string + local function mk_lua_list_string(xs) + if not xs or #xs == 0 then + return '{ }' + end + return "{ '" .. table.concat(xs, "', '") .. "' } " + end + + ---@param xs string[]? + ---@return string lua_multiline_string + local function mk_lua_multiline_str(xs) + if not xs or #xs == 0 then + return "''" + end + return '[[\n ' .. table.concat(xs, '\n ') .. ' \n]]' + end + + print('Using template: ' .. args.rockspec_template_file_path) + local rockspec_template_file = + assert(io.open(args.rockspec_template_file_path, 'r'), 'Could not open ' .. args.rockspec_template_file_path) + local content = rockspec_template_file:read('*a') + rockspec_template_file:close() + local repo_url = args.github_server_url .. '/' .. args.github_repo + local homepage = repo_url + local license + local repo_info_str, _ = + execute('curl -H "Accept: application/vnd.github+json" https://api.github.com/repos/' .. args.github_repo, print) + if repo_info_str and repo_info_str ~= '' then + local json = require('dkjson') + local repo_meta = json.decode(repo_info_str) + local repo_license = repo_meta.license or repo_meta.source and repo_meta.source.license + if args.license then + license = "license = '" .. args.license .. "'" + elseif repo_license and repo_license.spdx_id ~= '' and repo_license.spdx_id ~= 'NOASSERTION' then + license = "license = '" .. repo_license.spdx_id .. "'" + else + error([[ + Could not get the license SPDX ID from the GitHub API. + Please add a license file that GitHub can recognise or add a `license` input. + See: https://github.com/nvim-neorocks/luarocks-tag-release#license + ]]) + end + if not args.summary or args.summary == '' then + args.summary = repo_meta.description and repo_meta.description or '' + end + if not args.labels or #args.labels == 0 then + args.labels = repo_meta.topics and repo_meta.topics or {} + end + if repo_meta.homepage and repo_meta.homepage ~= '' then + homepage = repo_meta.homepage + end + end + + ---@param str string + ---@return string + local function escape_quotes(str) + local escaped = str:gsub("'", "\\'") + return escaped + end + + print( + 'Generating Luarocks release ' + .. modrev + .. ' for: ' + .. args.package_name + .. ' version ' + .. args.package_version + .. ' from ref ' + .. git_ref + .. '.' + ) + local rockspec = content + :gsub('$git_ref', git_ref) + :gsub('$modrev', modrev) + :gsub('$repo_url', repo_url) + :gsub('$archive_dir_suffix', archive_dir_suffix) + :gsub('$package', args.package_name) + :gsub('$summary', escape_quotes(args.summary)) + :gsub('$detailed_description', mk_lua_multiline_str(args.detailed_description_lines)) + :gsub('$dependencies', mk_lua_list_string(args.dependencies)) + :gsub('$labels', mk_lua_list_string(args.labels)) + :gsub('$homepage', homepage) + :gsub('$license', license) + :gsub('$copy_directories', mk_lua_list_string(args.copy_directories)) + :gsub('$build_type', args.build_type) + :gsub('$repo_name', args.repo_name) + + print('') + print('Generated rockspec:') + print('========================================================================================') + print(rockspec) + print('========================================================================================') + + luarocks_upload(rockspec) + + print('') + print('Done.') +end + +return luarocks_tag_release