IGA is the InvenioRDM GitHub Archiver, a standalone program as well as a GitHub Actions workflow that lets you automatically archive GitHub software releases in an InvenioRDM repository.
InvenioRDM is the basis for many institutional repositories such as CaltechDATA that enable users to preserve software and data sets in long-term archive. Though such repositories are critical resources, creating detailed records and uploading assets can be a tedious and error-prone process if done manually. This is where the InvenioRDM GitHub Archiver (IGA) comes in.
IGA creates metadata records and sends releases from GitHub to an InvenioRDM-based repository server. IGA can be invoked from the command line; it also can be set up as a GitHub Actions workflow to archive GitHub releases automatically for a repository each time they are made.
➜
IGA offers many notable features:
codemeta.json
and CITATION.cff
filesThe IGA GitHub action workflow automatically will add the archived DOI to a CodeMeta file and create/update a CFF file using the CodeMeta2CFF workflow.
IGA can be installed as either (or both) a command-line program on your computer or a GitHub Action in a GitHub repository.
Please choose an approach that suits your situation and preferences.
pipx
pip
Once you have installed iga
, the next steps are (1) get an InvenioRDM token and (2) configure iga
for running locally.
A GitHub Actions workflow is an automated process that runs on GitHub's servers under control of a file in your repository. Follow these steps to create the IGA workflow file:
In the main branch of your GitHub repository, create a .github/workflows
directory
In the .github/workflows
directory, create a file named (e.g.) iga.yml
and copy the following contents into it:
# GitHub Actions workflow for InvenioRDM GitHub Archiver version 1.3.4
# This is available as the file "sample-workflow.yml" from the open-
# source repository for IGA at https://github.com/caltechlibrary/iga/.
# ╭────────────────────────────────────────────╮
# │ Configure this section │
# ╰────────────────────────────────────────────╯
env:
# 👋🏻 Set the next variable to your InvenioRDM server address 👋🏻
INVENIO_SERVER: https://your-invenio-server.org
# Set to an InvenioRDM record ID to mark release as a new version.
parent_record: none
# The variables below are other IGA options. Please see the docs.
community: none
draft: false
all_assets: false
all_metadata: false
debug: false
# This variable is a setting for post-archiving CodeMeta file updates.
# If you don't have a CodeMeta file, you can remove the add_doi_codemeta
# and Codemeta2CFF jobs at the bottom of this file.
ref: main
# ╭────────────────────────────────────────────╮
# │ The rest of this file should be left as-is │
# ╰────────────────────────────────────────────╯
name: InvenioRDM GitHub Archiver
on:
release:
types: [published]
workflow_dispatch:
inputs:
release_tag:
description: The release tag (empty = latest)
parent_record:
description: ID of parent record (for versioning)
community:
description: Name of InvenioRDM community (if any)
draft:
description: Mark the record as a draft
type: boolean
all_assets:
description: Attach all GitHub assets
type: boolean
all_metadata:
description: Include additional GitHub metadata
type: boolean
debug:
description: Print debug info in the GitHub log
type: boolean
run-name: Archive ${{inputs.release_tag || 'latest release'}} in InvenioRDM
jobs:
run_iga:
name: Send to ${{needs.get_repository.outputs.server}}
runs-on: ubuntu-latest
needs: get_repository
outputs:
record_doi: ${{steps.iga.outputs.record_doi}}
steps:
- uses: caltechlibrary/iga@v1
id: iga
with:
INVENIO_SERVER: ${{env.INVENIO_SERVER}}
INVENIO_TOKEN: ${{secrets.INVENIO_TOKEN}}
all_assets: ${{github.event.inputs.all_assets || env.all_assets}}
all_metadata: ${{github.event.inputs.all_metadata || env.all_metadata}}
debug: ${{github.event.inputs.debug || env.debug}}
draft: ${{github.event.inputs.draft || env.draft}}
community: ${{github.event.inputs.community || env.community}}
parent_record: ${{github.event.inputs.parent_record || env.parent_record}}
release_tag: ${{github.event.inputs.release_tag || 'latest'}}
get_repository:
name: Get repository name
runs-on: ubuntu-latest
outputs:
server: ${{steps.parse.outputs.host}}
steps:
- name: Extract name from INVENIO_SERVER
id: parse
run: echo "host=$(cut -d'/' -f3 <<< ${{env.INVENIO_SERVER}} | cut -d':' -f1)" >> $GITHUB_OUTPUT
add_doi_codemeta:
name: "Add ${{needs.run_iga.outputs.record_doi}} to codemeta.json"
needs: run_iga
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ env.ref }}
- name: Install sde
run: pip install sde
- name: Add DOI to CodeMeta File
run: sde identifier ${{needs.run_iga.outputs.record_doi}} codemeta.json
- name: Commit CFF
uses: EndBug/add-and-commit@v9
with:
message: 'Add DOI to codemeta.json file'
add: 'codemeta.json'
CodeMeta2CFF:
runs-on: ubuntu-latest
needs: add_doi_codemeta
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ env.ref }}
- name: Convert CFF
uses: caltechlibrary/codemeta2cff@main
- name: Commit CFF
uses: EndBug/add-and-commit@v9
with:
message: 'Add updated CITATION.cff from codemeta.json file'
add: 'CITATION.cff'
Edit the value of the INVENIO_SERVER
variable (line 7 above) ↑
Optionally, change the values of other options (parent_record
, community
, etc.)
If you have a CodeMeta file, the GitHub action workflow can automatically add the DOI after IGA has run. The "ref" value is the branch where the CodeMeta file will be updated. If you don't use a CodeMeta file, you can delete the add_doi_codemeta
part of the workflow.
Save the file, commit the changes to git, and push your changes to GitHub Once you have installed the GitHub Action workflow for IGA, the next steps are (1) get an InvenioRDM token and (2) configure the GitHub Action workflow.
No matter whether IGA is run locally on your computer or as a GitHub Actions workflow, in both cases it must be provided with a personal access token (PAT) for your InvenioRDM server. Getting one is the first step.
To send a GitHub release to your InvenioRDM server, IGA needs this information:
The identity of the GitHub release is always given as an argument to IGA on the command line; the remaining values can be provided either via command-line options or environment variables. One approach is to set environment variables in shell scripts or your interactive shell. Here is an example using Bash shell syntax, with fake token values:
export INVENIO_SERVER=https://data.caltech.edu
export INVENIO_TOKEN=qKLoOH0KYf4D98PGYQGnC09hiuqw3Y1SZllYnonRVzGJbWz2
export GITHUB_TOKEN=ghp_wQXp6sy3AsKyyEo4l9esHNxOdo6T34Zsthz
Once these are set, use of IGA can be as simple as providing a URL for a release in GitHub. For example, the following command creates a draft record (the -d
option is short for --draft
) for another project in GitHub and tells IGA to open (the -o
option is short for --open
) the newly-created InvenioRDM entry in a web browser:
iga -d -o https://github.com/mhucka/taupe/releases/tag/v1.2.0
More options are described in the section on detailed usage information below.
After doing the GitHub Actions installation steps and obtaining an InvenioRDM token, one more step is needed: the token must be stored as a "secret" in your GitHub repository.
INVENIO_TOKEN
and paste in your InvenioRDM tokenAfter setting up the workflow and storing the InvenioRDM token in your repository on GitHub, it's a good idea to run the workflow manually to test that it works as expected.
true
Once the personal access token from InvenioRDM is stored as a GitHub secret, the workflow should run automatically every time a new release is made on GitHub – no further action should be needed. You can check the results (and look for errors if something went wrong) by going to the Actions tab in your GitHub repository.
This section provides detailed information about IGA's operation and options to control it.
The server address must be provided either as the value of the option --invenio-server
or in an environment variable named INVENIO_SERVER
. If the server address does not begin with https://
, IGA will prepend it automatically.
A personal access token (PAT) for making API calls to the InvenioRDM server must be also supplied when invoking IGA. The preferred method is to set the value of the environment variable INVENIO_TOKEN
. Alternatively, you can use the option --invenio-token
to pass the token on the command line, but you are strongly advised to avoid this practice because it is insecure.
To obtain a PAT from an InvenioRDM server, first log in to the server, then visit the page at /account/settings/applications
and use the interface there to create a token. The token will be a long string of alphanumeric characters such as OH0KYf4PGYQGnCM4b53ejSGicOC4s4YnonRVzGJbWxY
; set the value of the variable INVENIO_TOKEN
to this string.
It may be possible to run IGA without providing a GitHub access token. GitHub allows up to 60 API calls per minute when running without credentials, and though IGA makes several API calls to GitHub each time it runs, for some public repositories IGA will not hit the limit. However, if you are archiving a private repository, run IGA multiple times in a row, or the repository has many contributors, then you will need to supply a GitHub access token. The preferred way of doing that is to set the value of the environment variable GITHUB_TOKEN
. Alternatively, you can use the option --github-token
to pass the token on the command line, but you are strongly advised to avoid this practice because it is insecure. To obtain a PAT from GitHub, visit https://docs.github.com/en/authentication and follow the instructions for creating a "classic" personal access token.
Note that when you run IGA as a GitHub Actions workflow, you do not need to create or set a GitHub token because it is obtained automatically by the GitHub Actions workflow.
A GitHub release can be specified to IGA in one of two mutually-exclusive ways:
--account
and --repo
must be omitted.--account
and --repo
must be provided.Here's an example using approach #1 (assuming environment variables INVENIO_SERVER
, INVENIO_TOKEN
, and GITHUB_TOKEN
have all been set):
iga https://github.com/mhucka/taupe/releases/tag/v1.2.0
and here's the equivalent using approach #2:
iga --github-account mhucka --github-repo taupe v1.2.0
Note that when using this form of the command, the release tag (v1.2.0
above) must be the last item given on the command line.
The record created in InvenioRDM is constructed using information obtained using GitHub's API as well as several other APIs as needed. The information includes the following:
codemeta.json
file in the GitHub repositoryCITATION.cff
file in the GitHub repositoryIGA tries to use CodeMeta.json
first and CITATION.cff
second to fill out the fields of the InvenioRDM record. If neither of those files are present, IGA uses values from the GitHub repository instead. You can make it always use all sources of info with the option --all-metadata
. Depending on how complete and up-to-date your CodeMeta.json
and CITATION.cff
are, this may or may not make the record more comprehensive and may or may not introduce redundancies or unwanted values.
To override the auto-created metadata, use the option --read-metadata
followed by the path to a JSON file structured according to the InvenioRDM schema used by the destination server. When --read-metadata
is provided, IGA does not extract the data above, but still obtains the file assets from GitHub.
By default, IGA attaches to the InvenioRDM record only the ZIP file asset created by GitHub for the release. To make IGA attach all assets associated with the GitHub release, use the option --all-assets
.
To upload specific file assets and override the default selections made by IGA, you can use the option --file
followed by a path to a file to be uploaded. You can repeat the option --file
to upload multiple file assets. Note that if --file
is provided, then IGA does not use any file assets from GitHub; it is the user's responsibility to supply all the files that should be uploaded.
If both --read-metadata
and --file
are used, then IGA does not actually contact GitHub for any information.
To submit your record to a community, use the --community
option together with a community name. The option --list-communities
can be used to get a list of communities supported by the InvenioRDM server. Note that submitting a record to a community means that the record will not be finalized and will not be publicly visible when IGA finishes; instead, the record URL that you receive will be for a draft version, pending review by the community moderators.
If the --community
option is not used, then by default, IGA will finalize and publish the record. To make it stop short and leave the record as a draft instead, use the option --draft
. The draft option also takes precedence over the community option: if you use both --draft
and --community
, IGA will stop after creating the draft record and will not submit it to the community. (You can nevertheless submit the record to a community manually once the draft is created, by visiting the record's web page and using the InvenioRDM interface there.)
The option --parent-record
can be used to indicate that the record being constructed is a new version of an existing record. This will make IGA use the InvenioRDM API for record versioning. The newly-created record will be linked to a parent record identified by the value passed to --parent-record
. The value must be either an InvenioRDM record identifier (which is a sequence of alphanumeric characters of the form XXXXX-XXXXX, such as bknz4-bch35
, generated by the InvenioRDM server), or a URL to the landing page of the record in the InvenioRDM server. (Note that such URLs end in the record identifier.) Here is an example of using this option:
iga --parent-record xbcd4-efgh5 https://github.com/mhucka/taupe/releases/tag/v1.2.0
Running IGA with the option --save-metadata
will make it create a metadata record, but instead of uploading the record (and any assets) to the InvenioRDM server, IGA will write the result to the given destination. This can be useful not only for debugging but also for creating a starting point for a custom metadata record: first run IGA with --save-metadata
to save a record to a file, edit the result, then finally run IGA with the --read-metadata
option to use the modified record to create a release in the InvenioRDM server.
The --mode
option can be used to change the run mode. Four run modes are available: quiet
, normal
, verbose
, and debug
. The default mode is normal
, in which IGA prints a few messages while it's working. The mode quiet
will make it avoid printing anything unless an error occurs, the mode verbose
will make it print a detailed trace of what it is doing, and the mode debug
will make IGA even more verbose. In addition, in debug
mode, IGA will drop into the pdb
debugger if it encounters an exception during execution. On Linux and macOS, debug mode also installs a signal handler on signal USR1 that causes IGA to drop into the pdb
debugger if the signal USR1 is received. (Use kill -USR1 NNN
, where NNN is the IGA process id.)
By default, informational output is sent to the standard output (normally the terminal console). The option --log-dest
can be used to send the output to the given destination instead. The value can be -
(i.e., a dash) to indicate console output, or it can be a file path to send the output to the file. A special exception is that even if a log destination is given, IGA will still print the final record URL to stdout. This makes it possible to invoke IGA from scripts that capture the record URL while still saving diagnostic output in case debugging is needed.
By default, IGA prints only the record URL when done. The option --print-doi
will make it also print the DOI of the record. (Note that this only works when publishing records; if options --draft
or --community
are used, then there will be no DOI. In those case, only the URL will be printed.)
Reading and writing large files may take a long time; on the other hand, IGA should not wait forever on network operations before reporting an error if a server or network becomes unresponsive. To balance these conflicting needs, IGA automatically scales its network timeout based on file sizes. To override its adaptive algorithm and set an explicit timeout value, use the option --timeout
with a value in seconds.
If given the --version
option, this program will print its version and other information, and exit without doing anything else.
Running IGA with the option --help
will make it print help text and exit without doing anything else.
As explain above, IGA takes one required argument on the command line: either (1) the full URL of a web page on GitHub of a tagged release, or (2) a release tag name which is to be used in combination with options --github-account
and --github-repo
. The following table summarizes all the command line options available.
Long form option | Short | Meaning | Default | |
---|---|---|---|---|
--all-assets |
-A |
Attach all GitHub assets | Attach only the release source ZIP | |
--all-metadata |
-M |
Include additional metadata from GitHub | Favor CodeMeta & CFF | |
--community C |
-c C |
Submit record to RDM community C | Don't submit record to any community | |
--draft |
-d |
Mark the RDM record as a draft | Publish record when done | |
--file F |
-f F |
Upload local file F instead of GitHub assets | Upload only GitHub assets | ⚑ |
--github-account A |
-a A |
Look in GitHub account A | Get account name from release URL | ✯ |
--github-repo R |
-r R |
Look in GitHub repository R of account A | Get repo name from release URL | ✯ |
--github-token T |
-t T |
Use GitHub access token T | Use value in env. var. GITHUB_TOKEN |
|
--help |
-h |
Print help info and exit | ||
--invenio-server S |
-s S |
Send record to InvenioRDM server at address S | Use value in env. var. INVENIO_SERVER |
|
--invenio-token K |
-k K |
Use InvenioRDM access token K | Use value in env. var. INVENIO_TOKEN |
|
--list-communities |
-L |
List communities available for use with --community |
||
--log-dest L |
-l L |
Write log output to destination L | Write to terminal | ⚐ |
--mode M |
-m M |
Run in mode quiet , normal , verbose , or debug |
normal |
|
--open |
-o |
Open record's web page in a browser when done | Do nothing when done | |
--parent-record N |
-p N |
Make this a new version of existing record N | New record is unrelated to other records | ❖ |
--print-doi |
-i |
Print both the DOI & record URL when done | Print only the record URL | |
--read-metadata R |
-R R |
Read metadata record from file R; don\'t build one | Build metadata record | |
--save-metadata D |
-S D |
Save metadata record to file D; don\'t upload it | Upload to InvenioRDM server | |
--timeout X |
-T X |
Wait on network operations a max of X seconds | Auto-adjusted based on file size | |
--version |
-V |
Print program version info and exit |
⚑ Can repeat the option to specify multiple files.
⚐ To write to the console, use the character -
as the value of OUT; otherwise, OUT must be the name of a file where the output should be written.
✯ When using --github-account
and --github-repo
, the last argument on the command line must be a release tag name.
❖ The record identifier must be given either as a sequence of alphanumeric characters of the form XXXXX-XXXXX (e.g., bknz4-bch35
), or as a URL to the landing page of an existing record in the InvenioRDM server.
This program exits with a return status code of 0 if no problem is encountered. Otherwise, it returns a nonzero status code. The following table lists the possible values:
Code | Meaning |
---|---|
0 | success – program completed normally |
1 | interrupted |
2 | encountered a bad or missing value for an option |
3 | encountered a problem with a file or directory |
4 | encountered a problem interacting with GitHub |
5 | encountered a problem interacting with InvenioRDM |
6 | the personal access token was rejected |
7 | an exception or fatal error occurred |
Once you have set up the IGA workflow in your GitHub repository, you may wish to add a DOI badge to your repository's README file. It would be a chore to keep updating the DOI value in this badge every time a new release is made, and thankfully, it's not necessary: it's possible to make the badge get the current DOI value dynamically. Here is how:
10.22002/zsmem-2pg20
; the tail end is the zsmem-2pg20
part. (Your DOI and tail portion will be different.)Let SERVERURL
stand for the URL for your InvenioRDM server, and let IDENTIFIER
stand for the identifier portion of the DOI. In your README.md
file, write the DOI badge as follows (without line breaks):
[![DOI](https://img.shields.io/badge/dynamic/json.svg?label=DOI&query=$.pids.doi.identifier&uri=SERVERURL
/api/records/IDENTIFIER
/versions/latest)](SERVERURL
/records/IDENTIFIER
/latest)
For example, for CaltechDATA and the archived IGA releases there,
SERVERURL
= http://data.caltech.edu
IDENTIFIER
= zsmem-2pg20
which leads to the following badge string for IGA's README.md
file:
[![DOI](https://img.shields.io/badge/dynamic/json.svg?label=DOI&query=$.pids.doi.identifier&uri=https://data.caltech.edu/api/records/zsmem-2pg20/versions/latest)](https://data.caltech.edu/records/zsmem-2pg20/latest)
The result looks like this:
You can change the look of the badge by using style parameters. Please refer to the Shields.io documentation for static badges.
The following are known issues and limitations.
codemeta.json
or CITATION.cff
file (which allow authors more control over how they want their names represented), IGA is forced to try to split the single GitHub name string into two parts. A foolproof algorithm for doing this does not exist, so IGA will sometimes get it wrong. (That said, IGA goes to extraordinary lengths to try to do a good job.)codemeta.json
and CITATION.cff
files. GitHub also provides a flag that is meant to be used to label organizational accounts, but sometimes people don't set the GitHub account information correctly. Consequently, if IGA has to use GitHub data to get (e.g.) the list of contributors on a project, it may mislabel identities in the InvenioRDM record it produces.codemeta.json
files; neither GitHub nor CITATION.cff
have provisions to store this kind of metadata. The CodeMeta specification defines two fields for this purpose: funder
and funding
. Unfortunately, these map imperfectly to the requirements of InvenioRDM's metadata format. In addition, people don't always follow the CodeMeta guidelines, and sometimes they write funding information as text strings (instead of structured objects), the interpretation of which would require software that can recognize grant and funding agency information from free-text descriptions. This combination of factors means IGA often can't fill in the funding metadata in InvenioRDM records even if there is some funding information in the codemeta.json
file.If you find an issue, please submit it in the GitHub issue tracker for this repository.
Your help and participation in enhancing IGA is welcome! Please visit the guidelines for contributing for some tips on getting started.
Software produced by the Caltech Library is Copyright © 2022–2024 California Institute of Technology. This software is freely distributed under a BSD-style license. Please see the LICENSE file for more information.
This work was funded by the California Institute of Technology Library.
IGA uses multiple other open-source packages, without which it would have taken much longer to write the software. I want to acknowledge this debt. In alphabetical order, the packages are:
pytest
mock
package for use with pytest
setup.py