Skip to content

make_release.py

sy2002 edited this page Jul 4, 2026 · 2 revisions

Packaging releases with make_release.py

This guide explains how to use the generic MiSTer2MEGA65 (M2M) make_release.py script to package a core release, and how to adapt the script to your own core without forking its release logic.

The important idea is this:

make_release.py is the release engine. CORE/release.toml is your core-specific identity card.

The Python script knows the standard M2M repository layout and the release workflow. Your core tells the script only what actually differs between cores: the public core name, file naming scheme, .cor metadata, config-file name, and a few release policies.


1. What the script does, and what it deliberately does not do

make_release.py packages files that already exist. It does not run Vivado. You still synthesize and implement your core first, once per supported MEGA65 board revision.

The script does this:

  1. Reads CORE/release.toml.
  2. Optionally imports CORE/release_hooks.py.
  3. Checks the version name you passed on the command line.
  4. Checks that the version in CORE/vhdl/config.vhd matches, if configured.
  5. For alpha releases, checks doc/inofficial.md and verifies the listed git commit.
  6. Selects the requested board revisions.
  7. Finds the already-built Vivado bitstreams.
  8. Creates a release folder.
  9. Copies each .bit file into the release folder.
  10. Converts each .bit into a .cor file using coretool or bit2core.
  11. Generates the M2M Shell config file using M2M/tools/make_config.sh.
  12. Copies release notes such as VERSIONS.md and, for alpha releases, inofficial.md.
  13. Runs optional core-specific post-packaging hooks.
  14. Prints a concise summary.

The script does not do this:

  • It does not synthesize, implement, or generate bitstreams in Vivado.
  • It does not decide whether your core is ready to release.
  • It does not write your VERSIONS.md.
  • It does not tag git.
  • It does not upload to GitHub or to the MEGA65 Filehost.
  • It does not create the final Filehost ZIP by itself.

So think of it as the mechanical packaging step between "Vivado bitstreams are ready" and "I am going to publish this release".


2. The standard M2M layout assumed by the script

The script intentionally hardcodes the M2M conventions that should be the same for every core:

Purpose Path or convention
Core-specific release config CORE/release.toml
Optional core-specific Python hooks CORE/release_hooks.py
Core configuration VHDL CORE/vhdl/config.vhd
Shell config generator M2M/tools/make_config.sh
R3 bitstream CORE/CORE-R3.runs/impl_1/mega65_r3.bit
R4 bitstream CORE/CORE-R4.runs/impl_1/mega65_r4.bit
R5 bitstream CORE/CORE-R5.runs/impl_1/mega65_r5.bit
R6 bitstream CORE/CORE-R6.runs/impl_1/mega65_r6.bit
MEGA65 machine name for .cor conversion mega65r3, mega65r4, mega65r5, mega65r6
Release notes VERSIONS.md
Alpha release table doc/inofficial.md

This is why CORE/release.toml can stay small. A core maintainer should not need to describe where Vivado puts the R6 bitstream if the framework already standardizes that.


3. The mental model: one stable engine, one small core config

The release tool has three layers:

  make_release.py                 CORE/release.toml              CORE/release_hooks.py
  stable release engine           core identity + policy          optional custom logic
  ┌────────────────────┐          ┌────────────────────┐          ┌───────────────────┐
  │ parse CLI          │  reads   │ file_base          │  imports │ validate(ctx)     │
  │ check version      │─────────►│ display_name       │─────────►│ after_package(ctx)│
  │ find bitstreams    │          │ .cor metadata      │          └───────────────────┘
  │ run coretool       │          │ config filename    │
  │ copy release docs  │          │ docs policy        │
  └────────────────────┘          └────────────────────┘

Most cores should only edit CORE/release.toml. Keep CORE/release_hooks.py empty unless your core genuinely needs an extra check or extra artifact.

The design goal is simple: do not maintain a per-core fork of make_release.py. If two cores change the Python script independently, the release process will drift again. If two cores only change CORE/release.toml, they still share the same tested release engine.


4. Preparing a release

Before you run the script, do the normal release work.

4.1 Update the version in the core

The recommended M2M convention is to keep the version string in exactly one place in CORE/vhdl/config.vhd:

constant CORE_VERSION : string := "V1";

Then derive the user-visible strings and config-file name from that constant:

constant CFG_FILE : string := "/amiga/aexp-" & CORE_VERSION & ".cfg";
constant CORENAME : string := "Amiga 500 for MEGA65 " & CORE_VERSION;

The exact directory and filename are core-specific. The pattern matters more than the example: one version constant, used everywhere.

Why this is worth doing:

  • the welcome screen shows the same version you package,
  • the serial/debug core name shows the same version,
  • the .cor metadata shows the same version,
  • the generated Shell config file matches the versioned CFG_FILE, and
  • the release script can catch accidental mismatches before you publish.

If your core does not yet have a version variable, see section 10.2 below.

4.2 Update VERSIONS.md

VERSIONS.md is what users read to understand what changed, what is known to work, and what limitations remain. The script can copy it, but it cannot write a good changelog for you.

For a mature core, VERSIONS.md is normally required. For a very early core, you may configure it as optional in CORE/release.toml, but that should be a temporary state.

4.3 For alpha releases: update doc/inofficial.md

Alpha versions use this naming scheme:

WIP-V<n>-A<m>
WIP-V<n>-A<m>X<k>

Examples:

WIP-V6-A18
WIP-V1-A3
WIP-V6-A13X1

For these versions, the script normally checks that doc/inofficial.md contains a row for the alpha and that the commit listed in that row exists in the local git repository.

This is a useful guard: alpha packages should be traceable to an exact commit.

4.4 Build the Vivado bitstreams

Open the Vivado project for each board revision you want to ship and generate the bitstream:

./CORE/run_vivado_r3.sh
./CORE/run_vivado_r4.sh
./CORE/run_vivado_r5.sh
./CORE/run_vivado_r6.sh

The script expects the generated files here:

CORE/CORE-R3.runs/impl_1/mega65_r3.bit
CORE/CORE-R4.runs/impl_1/mega65_r4.bit
CORE/CORE-R5.runs/impl_1/mega65_r5.bit
CORE/CORE-R6.runs/impl_1/mega65_r6.bit

When packaging all configured boards, the script warns if a bitstream was not built today. That is only a warning, because there are valid reasons to package older bitstreams, but it catches the common mistake "I rebuilt R6 and forgot R3".

4.5 Make sure coretool or bit2core is available

The script can use either of the MEGA65 tools:

  • coretool - preferred when available,
  • bit2core - fallback.

The tool may be on PATH, or it may be defined by a shell alias in your normal bash/zsh startup files. The script checks both cases.

If both are available, coretool is used. This matters because coretool supports the modern metadata flags directly, while bit2core uses its older tail argument syntax.

Preferably, use coretool. Get it here.


5. Running the script

The command shape is:

./make_release.py <version> <output-parent-folder> [targets] [options]

Examples:

./make_release.py V1 ~/Desktop
./make_release.py V1.1 /tmp/builds R3,R6
./make_release.py WIP-V6-A18 /tmp/builds R6
./make_release.py A15test1 /tmp/builds R6 --ignore
./make_release.py V1.1 /tmp/builds R3,R4,R5,R6 --force --verbatim

The second argument is a parent folder. The script creates a release subfolder inside it:

<output-parent-folder>/<file_base>-<version>/

For C64MEGA65:

/tmp/builds/C64MEGA65-WIP-V6-A18/

For AExp:

/tmp/builds/AExp-WIP-V1-A3/

5.1 Version names

Without --ignore, the script accepts these version forms:

Type Pattern Example
Major release V<n> V6
Minor release V<n>.<m> V6.1
Alpha release WIP-V<n>-A<m>[X<k>] WIP-V6-A18, WIP-V6-A13X1

If you pass --ignore, the grammar check is skipped. This is useful for local test packages:

./make_release.py A15test1 /tmp/builds R6 --ignore

Important: --ignore does not bypass the configured version-source check. If CORE/release.toml says that CORE_VERSION must match, then CORE_VERSION must still be A15test1. This is intentional. It prevents a test package whose filename says one thing while the core itself reports another.

5.2 Target selection

If you omit the target argument, all configured boards are packaged:

./make_release.py V1 /tmp/builds

To package only some boards:

./make_release.py V1 /tmp/builds R6
./make_release.py V1 /tmp/builds R3,R6
./make_release.py V1 /tmp/builds R3+R4+R5+R6

Accepted separators are comma, plus, slash, semicolon, and whitespace.

The target names are always the board revisions:

R3 R4 R5 R6

5.3 Useful options

Option Meaning
--force Allow writing into an existing non-empty release folder.
--ignore Accept an ad-hoc version name and skip alpha-row checks.
--verbatim Print every step and external command output.

Without --verbatim, the script keeps the console quiet and prints a final summary. Use --verbatim while setting up a new core or debugging a release.


6. What the output folder contains

For each board, the script copies the .bit file and creates a .cor file. The names are based on file_base, the version, and the board revision:

<file_base>-<version>-<rev>.bit
<file_base>-<version>-<rev>.cor

For C64MEGA65 WIP-V6-A18 and target R6:

C64MEGA65-WIP-V6-A18-R6.bit
C64MEGA65-WIP-V6-A18-R6.cor

For AExp WIP-V1-A3 and target R3:

AExp-WIP-V1-A3-R3.bit
AExp-WIP-V1-A3-R3.cor

If Shell config generation is enabled, the script also creates the config file that stores OSM settings persistently. The filename comes from CORE/release.toml.

For C64MEGA65:

c64mega65-WIP-V6-A18

For AExp:

aexp-WIP-V1-A3.cfg

The file is generated by:

M2M/tools/make_config.sh <output-file> auto

The auto mode reads OPTM_SIZE from CORE/vhdl/config.vhd and writes exactly that many bytes. This is why the generated config file must be regenerated whenever the menu layout changes.

The script also copies:

  • VERSIONS.md, depending on policy,
  • doc/inofficial.md for alpha releases, depending on policy,
  • any extra files created by CORE/release_hooks.py.

7. How the script converts .bit to .cor

The .cor file is what users install into a MEGA65 core slot. The script creates it by calling coretool or bit2core.

The board revision controls the target machine:

Board Tool target
R3 mega65r3
R4 mega65r4
R5 mega65r5
R6 mega65r6

The public core name and version are written into the .cor metadata:

display_name = "C64 for MEGA65"
version      = "WIP-V6-A18"

Some cores also need file association metadata. The C64 core, for example, registers itself as the default handler for cartridge files:

[cor]
bit2core_tail = "=default,c64cart+c64cart"
coretool_flags = "c64cart"
coretool_caps = "c64cart,default"

A core that has no MEGA65-registered file type can leave these empty:

[cor]
bit2core_tail = ""
coretool_flags = ""
coretool_caps = ""

Do not copy the C64 cartridge metadata blindly. It is correct for the C64 core because C64 cartridge files are meant to be associated with that core. It is wrong for a core whose files are mounted from inside the core's own OSM.


8. The release checklist: from bitstreams to public release

A practical release flow looks like this.

Step 1 - Update core-visible version

In CORE/vhdl/config.vhd:

constant CORE_VERSION : string := "V1";

Make sure CORENAME, welcome/help screens, and CFG_FILE derive from it or are otherwise manually updated.

Step 2 - Update release notes

Update VERSIONS.md.

For an alpha release, add or update the row in doc/inofficial.md.

Step 3 - Build every target board

Generate the bitstreams in Vivado for every board revision you intend to ship.

For a full modern release, that usually means:

R3, R4, R5, R6

R3 and R3A use the same binary.

Step 4 - Run a targeted smoke package

Package one board first:

./make_release.py WIP-V1-A3 /tmp/release-smoke R6 --verbatim

Check that:

  • the version check passes,
  • the expected .bit file is copied,
  • the .cor file is generated,
  • the config file has the expected name,
  • VERSIONS.md is copied,
  • the final summary looks right.

Step 5 - Test the .cor

Install the generated .cor on real hardware through the MEGA65 core slot menu and boot it the way a user will boot it.

JTAG testing is not a substitute for this. JTAG proves the bitstream can run; the .cor test proves the release package can be installed and booted.

Step 6 - Package all boards

Run the full package:

./make_release.py V1 /tmp/releases

or explicitly:

./make_release.py V1 /tmp/releases R3,R4,R5,R6

Step 7 - Create the user-facing release archive

The script creates the release folder, not the public ZIP strategy. Before uploading, make sure the package layout makes sense for users:

  • .cor files for supported boards,
  • .bit files if you choose to ship them,
  • generated config file if your core uses persistent settings,
  • concise install notes if the download is meant to stand alone,
  • links to the full user manual.

Then follow the broader release process:

  • tag git,
  • create a GitHub release,
  • upload to the MEGA65 Filehost,
  • announce the release to the community.

That broader process is covered by XYZ. How to release your core to the MEGA65 community.


9. CORE/release.toml

This is the file a core maintainer normally edits.

9.1 C64MEGA65 example

[core]
file_base = "C64MEGA65"
display_name = "C64 for MEGA65"

[version]
check = "core_version_constant"
file = "CORE/vhdl/config.vhd"
constant = "CORE_VERSION"

[cor]
bit2core_tail = "=default,c64cart+c64cart"
coretool_flags = "c64cart"
coretool_caps = "c64cart,default"

[shell_config]
enabled = true
filename = "c64mega65-{version}"

[release_notes]
versions_md = "required"

[alpha]
inofficial_md = "required"

[cleanup]
stale_files = ["c64mega65"]

9.2 AExp example

[core]
file_base = "AExp"
display_name = "Amiga 500 for MEGA65"

[version]
check = "core_version_constant"
file = "CORE/vhdl/config.vhd"
constant = "CORE_VERSION"

[cor]
bit2core_tail = ""
coretool_flags = ""
coretool_caps = ""

[shell_config]
enabled = true
filename = "aexp-{version}.cfg"

[release_notes]
versions_md = "optional"

[alpha]
inofficial_md = "required"

[cleanup]
stale_files = []

9.3 [core]

[core]
file_base = "AExp"
display_name = "Amiga 500 for MEGA65"
boards = ["R3", "R4", "R5", "R6"]

file_base is used for the release folder and generated .bit/.cor names:

AExp-WIP-V1-A3/
AExp-WIP-V1-A3-R3.bit
AExp-WIP-V1-A3-R3.cor

display_name is written into the .cor metadata:

Amiga 500 for MEGA65

boards is optional. If omitted, the default is:

boards = ["R3", "R4", "R5", "R6"]

Only use a smaller list if your core deliberately supports fewer board revisions.

9.4 [version]

Recommended:

[version]
check = "core_version_constant"
file = "CORE/vhdl/config.vhd"
constant = "CORE_VERSION"

This checks for:

constant CORE_VERSION : string := "V1";

and requires it to match the command-line version.

Alternative for non-standard naming:

[version]
check = "regex"
file = "CORE/vhdl/config.vhd"
pattern = 'constant\s+MY_CORE_VERSION\s*:\s*string\s*:=\s*"([^"]+)"'

The regex must have one capture group: the version string.

Opt-out:

[version]
check = "none"

Use this only for older cores that do not yet expose a version string in VHDL. The script will still package the command-line version, but it will warn that the version was not verified against the core.

9.5 [cor]

[cor]
bit2core_tail = ""
coretool_flags = ""
coretool_caps = ""

These values control .cor metadata.

Use empty strings unless your core needs MEGA65 file associations.

For C64 cartridge support:

[cor]
bit2core_tail = "=default,c64cart+c64cart"
coretool_flags = "c64cart"
coretool_caps = "c64cart,default"

The script chooses the right syntax depending on whether it runs coretool or bit2core.

9.6 [shell_config]

[shell_config]
enabled = true
filename = "aexp-{version}.cfg"

If enabled = true, the script creates this file by running:

M2M/tools/make_config.sh <filename> auto

The {version} placeholder is replaced by the command-line version.

Make sure this basename matches the basename in your VHDL CFG_FILE.

Example:

constant CFG_FILE : string := "/amiga/aexp-" & CORE_VERSION & ".cfg";

then:

filename = "aexp-{version}.cfg"

The generated release artifact is only the file itself:

aexp-V1.cfg

Your user documentation tells the user to put it into:

/amiga/aexp-V1.cfg

If your core does not use persistent OSM settings:

[shell_config]
enabled = false
filename = ""

9.7 [release_notes]

[release_notes]
versions_md = "required"

Valid policies:

Policy Meaning
required Missing file is a release error.
optional Missing file prints a warning and packaging continues.
skip Do not copy this artifact.

Use required for mature cores.

9.8 [alpha]

[alpha]
inofficial_md = "required"

This controls alpha release handling for doc/inofficial.md.

For required, an alpha release such as WIP-V1-A3 must have a row in doc/inofficial.md, and the commit hash in that row must resolve in git.

For optional, missing rows become warnings. For skip, the check and copy are disabled.

9.9 [cleanup]

[cleanup]
stale_files = ["c64mega65"]

This removes old generated artifacts from the release folder before writing new ones. C64MEGA65 uses this to remove the old unversioned c64mega65 config file when reusing an output folder with --force.

Most new cores can leave this empty:

stale_files = []

10. Versioning policy

10.1 The recommended convention

Use CORE_VERSION in CORE/vhdl/config.vhd:

constant CORE_VERSION : string := "V1";

Then derive all user-visible versioned strings from it:

constant CFG_FILE : string := "/mycore/mycore-" & CORE_VERSION & ".cfg";
constant CORENAME : string := "My Core for MEGA65 " & CORE_VERSION;

Also use it in welcome and help screens where appropriate:

"My Core for MEGA65 - " & CORE_VERSION & "\n"

This makes the release habit one line:

When making a release, update CORE_VERSION, then package that exact same version.

10.2 What if my core has no CORE_VERSION constant?

You have three options.

Best option: add the convention.

This is the cleanest long-term solution. Add:

constant CORE_VERSION : string := "V1";

and wire CFG_FILE, CORENAME, welcome screens, and help screens to it.

Transition option: use a custom regex.

If your core already has a version string under a different name:

[version]
check = "regex"
file = "CORE/vhdl/config.vhd"
pattern = 'constant\s+MY_VERSION\s*:\s*string\s*:=\s*"([^"]+)"'

Last-resort option: opt out.

[version]
check = "none"

The script will package the version from the command line but cannot prove that the core itself reports the same version. This is acceptable for older cores during migration, but it should not be the final state for a maintained M2M core.


11. Optional hooks: CORE/release_hooks.py

Most cores do not need hooks. Keep the file boring:

def validate(ctx):
    return None


def after_package(ctx):
    return []

Hooks are there for genuine core-specific behavior that does not belong in CORE/release.toml.

11.1 validate(ctx)

validate(ctx) runs after normal preflight checks and before the release folder is written.

Use it for extra release blockers. Examples:

  • check that a mandatory ROM checksum document exists,
  • check that a generated handover file exists,
  • reject packaging a release from a forbidden branch,
  • check that a core-specific binary blob was rebuilt.

Example:

def validate(ctx):
    required = ctx.repo / "doc" / "release-checklist.md"
    if not required.is_file():
        ctx.die("doc/release-checklist.md is missing.")

11.2 after_package(ctx)

after_package(ctx) runs after the standard package was written. It may create extra artifacts and return their paths for the final summary.

Example:

def after_package(ctx):
    src = ctx.repo / "README.md"
    dst = ctx.out / "README.md"
    ctx.copy_preserving_timestamps(src, dst)
    return [dst]

11.3 What is in ctx?

The hook context contains:

Field Meaning
ctx.repo Repository root path.
ctx.out Release output folder.
ctx.version Command-line version.
ctx.kind major, minor, alpha, or ignored.
ctx.targets Selected board revisions.
ctx.config Parsed ReleaseConfig.
ctx.force Whether --force was passed.
ctx.ignore Whether --ignore was passed.
ctx.verbatim Whether --verbatim was passed.

Helper methods:

Method Meaning
ctx.warn(msg) Emit a warning and include it in the summary.
ctx.die(msg) Abort the release with an error.
ctx.copy_preserving_timestamps(src, dst) Copy a file like the standard release artifacts.

Keep hooks small. If a hook grows into a second release script, the abstraction has failed.


12. Adapting the release tool to a new core

For a new core, do this.

Step 1 - Copy the generic script

Use the current generic make_release.py from an existing M2M core that already uses CORE/release.toml, for example C64MEGA65 or AExp.

Do not edit the script unless you are improving the shared release engine for all cores.

Step 2 - Create CORE/release.toml

Start from this minimal template:

[core]
file_base = "MyCore"
display_name = "My Core for MEGA65"

[version]
check = "core_version_constant"
file = "CORE/vhdl/config.vhd"
constant = "CORE_VERSION"

[cor]
bit2core_tail = ""
coretool_flags = ""
coretool_caps = ""

[shell_config]
enabled = true
filename = "mycore-{version}.cfg"

[release_notes]
versions_md = "required"

[alpha]
inofficial_md = "required"

[cleanup]
stale_files = []

Change only what is true for your core.

Step 3 - Add CORE_VERSION

In CORE/vhdl/config.vhd:

constant CORE_VERSION : string := "V1";

Derive CFG_FILE and CORENAME from it.

Step 4 - Create CORE/release_hooks.py

Start empty:

def validate(ctx):
    return None


def after_package(ctx):
    return []

Only add logic when your core genuinely needs it.

Step 5 - Check help output

Run:

./make_release.py --help

Confirm that the generated help shows your core name, file base, configured boards, and config filename pattern.

Step 6 - Run one targeted smoke package

After building at least one bitstream:

./make_release.py WIP-V1-A1 /tmp/mycore-release-smoke R6 --ignore --verbatim

Use the version that is actually in CORE_VERSION.

If that works, package all boards.


13. Common failure modes

Version mismatch

Error:

Version mismatch: command line says 'V1' but CORE/vhdl/config.vhd has 'V0.9'.

Fix: update CORE_VERSION, or call the script with the version that is actually in the core.

Missing bitstream

Error:

Missing bitstream(s): CORE/CORE-R6.runs/impl_1/mega65_r6.bit.

Fix: open the matching Vivado project and generate the bitstream.

No coretool or bit2core

Error:

'coretool' or 'bit2core' not found.

Fix: install mega65-tools, add the tool to PATH, or define a shell alias that your bash/zsh startup files can resolve.

Stale bitstream warning

Warning:

Some bitstreams were not built today - you may be packaging an outdated release.

Fix: rebuild the listed board if that was not intentional. The script warns but does not abort.

Config file has the wrong size on the user's SD card

Symptom: OSM settings are not saved or not loaded.

Cause: the generated config file must be exactly OPTM_SIZE bytes. If you add, remove, or reorder OSM lines, regenerate and redistribute the config file.

The release script does this automatically. Users still need to copy the new file to the SD-card path named by CFG_FILE.

Wrong .cor file associations

Symptom: the MEGA65 associates a file type with the wrong core, or a core claims capabilities it should not claim.

Cause: copied [cor] metadata from another core.

Fix: use empty bit2core_tail, coretool_flags, and coretool_caps unless your core intentionally registers file associations.


14. A complete example: C64 alpha package

C64 has:

[core]
file_base = "C64MEGA65"
display_name = "C64 for MEGA65"

[shell_config]
filename = "c64mega65-{version}"

and in config.vhd:

constant CORE_VERSION : string := "WIP-V6-A18";
constant CFG_FILE     : string := "/c64/c64mega65-" & CORE_VERSION;

After building R6 in Vivado:

./make_release.py WIP-V6-A18 /tmp/release-smoke R6 --verbatim

The output folder is:

/tmp/release-smoke/C64MEGA65-WIP-V6-A18/

It contains:

C64MEGA65-WIP-V6-A18-R6.bit
C64MEGA65-WIP-V6-A18-R6.cor
c64mega65-WIP-V6-A18
VERSIONS.md
inofficial.md

The generated config file basename is c64mega65-WIP-V6-A18. The user places it in /c64/ on the SD card because CFG_FILE says:

/c64/c64mega65-WIP-V6-A18

15. A complete example: AExp alpha package

AExp has:

[core]
file_base = "AExp"
display_name = "Amiga 500 for MEGA65"

[shell_config]
filename = "aexp-{version}.cfg"

and in config.vhd:

constant CORE_VERSION : string := "WIP-V1-A3";
constant CFG_FILE     : string := "/amiga/aexp-" & CORE_VERSION & ".cfg";

After building R3 in Vivado:

./make_release.py WIP-V1-A3 /tmp/release-smoke R3 --verbatim

The output folder is:

/tmp/release-smoke/AExp-WIP-V1-A3/

It contains:

AExp-WIP-V1-A3-R3.bit
AExp-WIP-V1-A3-R3.cor
aexp-WIP-V1-A3.cfg
VERSIONS.md
inofficial.md

The generated config file basename is aexp-WIP-V1-A3.cfg. The user places it in /amiga/ on the SD card because CFG_FILE says:

/amiga/aexp-WIP-V1-A3.cfg

16. Final rule of thumb

If you are maintaining a core, you should normally touch these files for release packaging:

CORE/release.toml
CORE/release_hooks.py       only if really needed
CORE/vhdl/config.vhd        CORE_VERSION and CFG_FILE convention
VERSIONS.md
doc/inofficial.md           for alpha releases

You should normally not touch:

make_release.py

Treat the Python file as shared infrastructure. Improve it when the framework needs a better release engine, not when one core needs a different name.

Clone this wiki locally