(micro) Howto migrate from core20 to core22

core22 support in Snapcraft 7.0 introduces new underlying layers and concepts that require some changes when migrating from core20 to core22; support for core20 and other bases is unaffected by Snapcraft 7.0.

Parts

Snapcraft has moved to a more general syntactical mechanism that can be used consistently across the *craft ecosystem. Most of the following changes are a reflection of that alignment.

While snapcraftctl is still supported, the new craftctl is now preferred.

Overriding pull

override-pull: |
    snapcraftctl pull

translates to

override-pull: |
    craftctl default

Overriding build

override-build: |
    snapcraftctl build

translates to

override-build: |
    craftctl default

Overriding stage

override-stage: |
    snapcraftctl stage

translates to

override-stage: |
    craftctl default

Overriding prime

override-prime: |
    snapcraftctl prime

translates to

override-prime: |
    craftctl default

Setting a version

override-<step>: |
    snapcraftctl set-version 1.0.0

translates to

override-<step>: |
    craftctl set version=1.0.0

Setting a grade

override-<step>: |
    snapcraftctl set-grade stable

translates to

override-<step>: |
    craftctl set grade=stable

Grammar

The try keyword is no longer available, instead of trying use the architecture specific entry for a predictable result such that

stage-packages:
    - try:
        - criu

now uses the architecture specific on entry

stage-packages:
    - on amd64:
        - criu

Architectures

The keywords for architectures are now build-on and build-for:

architectures:
  - build-on: [amd64]
    run-on: [arm64]

translates to

architectures:
  - build-on: [amd64]
    build-for: [arm64]

Environment variables

These environment variables are still supported but should be migrated to the following:

  • SNAPCRAFT_PART_SRC_WORKCRAFT_PART_SRC_WORK
  • SNAPCRAFT_PART_SRCCRAFT_PART_SRC
  • SNAPCRAFT_PROJECT_DIRCRAFT_PROJECT_DIR
  • SNAPCRAFT_PART_BUILDCRAFT_PART_BUILD
  • SNAPCRAFT_PROJECT_NAMECRAFT_PROJECT_NAME
  • SNAPCRAFT_PART_BUILD_WORKCRAFT_PART_BUILD_WORK
  • SNAPCRAFT_ARCH_TRIPLETCRAFT_ARCH_TRIPLET
  • SNAPCRAFT_PARALLEL_BUILD_COUNTCRAFT_PARALLEL_BUILD_COUNT
  • SNAPCRAFT_PRIMECRAFT_PRIME
  • SNAPCRAFT_TARGET_ARCHCRAFT_TARGET_ARCH
  • SNAPCRAFT_STAGECRAFT_STAGE
  • SNAPCRAFT_PART_NAMECRAFT_PART_NAME
  • SNAPCRAFT_PART_INSTALLCRAFT_PART_INSTALL

Getting the grade

While SNAPCRAFT_PROJECT_GRADE is still supported, craftctl get grade is now preferred.

Getting the version

While SNAPCRAFT_PROJECT_VERSION is still supported, craftctl get version is now preferred.

Unbound variable verification

Snapcraft will now report errors in case of unbound variables in user scriptlets and in variables set by the user in build-environment. A typical situation this can happen is if LD_LIBRARY_PATH is extended and no previous value is set. In this case, the :+ parameter expansion syntax can be used (such as in ${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} to only expand if the variable is set), or just set the new value since there’s no previous value assigned to the variable.

part:
  user-part:
    ...
    build-environment:
      - LD_LIBRARY_PATH: $CRAFT_STAGE/usr/lib/$CRAFT_ARCH_TRIPLET${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}

Filesets

The filesets keyword is no longer supported in snapcraft.yaml. Instead, files and directories to include or exclude should be defined with the stage and prime keywords for a part.

Application defaults

Snapcraft provided environment

Snapcraft used to setup a snap.yaml that looked like the following:

apps:
    <user-defined-app>:
        command-chain: [snap/snapcraft-runner.sh]
        command: <user-defined-command>

This was not overridable and to get rid of it, there was a legacy way of dealing with this, which was to define the following in snapcraft.yaml

apps:
    <user-defined-app>:
        adapter: none
        command: <user-defined-command>

Snapcraft has moved to defining an environment for each application entry instead of setting up a command-chain, with a simple way to override or disable.

Default behavior

snapcraft.yaml has no entries in the root environment, then snap.yaml will have

environment:
    LD_LIBRARY_PATH: <snapcraft-value>
    PATH: <snapcraft-value>

Overriding an entry

A user can override one of these by defining it, such that if they define PATH like,

environment:
    PATH: <user-value>

Then snap.yaml will have

environment:
    LD_LIBRARY_PATH: <snapcraft-value>
    PATH: <user-value>

Nulling an entry

A user can nullify an entry by using a YAML null entry, such that if they define PATH like,

environment:
    PATH: null

Then snap.yaml will have

environment:
    LD_LIBRARY_PATH: <snapcraft-value>

Plugins

Most plugins do not install the base dependency by default anymore to allow more control when building.

Go plugin

go is no longer installed by default, to use the snap of go from the latest/stable channel do:

parts:
    user-part:
        source: .
        plugin: go
        build-snaps: [go/latest/stable]

to install from the deb:

parts:
    user-part:
        source: .
        plugin: go
        build-packages: [golang-go]

to build go from source:

parts:
    user-part:
        source: .
        plugin: go
        after: [go-deps]
    go-deps:
        source: ...
        plugin: ...

Rust plugin

rustc and cargo are no longer installed by default, to install the deb do:

parts:
    user-part:
        source: .
        plugin: rust
        build-packages: [cargo, rustc]

NPM plugin

node and npm are no longer installed by default, to use the node snap do:

parts:
    user-part:
        source: .
        plugin: npm
        build-snaps: [node/16/stable]

to include the node binary in the actual build (and provide npm), do

parts:
    user-part:
        source: .
        plugin: npm
        npm-include-node: true

Meson plugin

meson is no longer installed by default, to use the meson deb do:

parts:
    user-part:
        source: .
        plugin: meson
        build-packages: [meson, ninja-build]

to build meson from an alternate source:

parts:
    user-part:
        source: .
        plugin: meson
        after: [meson-deps]
    meson-deps:
        plugin: nil
        override-build: |
            pip install meson

Python plugin

The following environment variable names should be migrated:

  • SNAPCRAFT_PYTHON_INTERPRETERPARTS_PYTHON_INTERPRETER
  • SNAPCRAFT_PYTHON_VENV_ARGSPARTS_PYTHON_VENV_ARGS

Destructive Mode

sudo is no longer run on behalf of the user, it was a leftover from the pre-containerization era and considered redundant with build instance usage since Snapcraft already runs as root in the managed environment.

Additionally, to avoid any interaction during the lifecycle processing to prevent blocking, and the CLI library in today’s UI reflects that by not implementing support to user input. If required, use sudo in the call to Snapcraft itself.

2 Likes

There are some conflicting information. The beginning of this thread and the Call for testing: Snapcraft 7 imply that the migration from snapcraftctl and SNAPCRAFT_ environment variables is REQUIRED, however, later on claim that they are preferred or recommended.

It would be good to know at which version of snapcraft the support for the former will he stopped.

Could you clarify when the store will start supporting core22?

It is recommended only, at the time of writing of those notes it was originally required (I can amend with a note)

the store should already support this

1 Like

I would like to see extended overrides of “append” and “prepend”, as in, “append-pull”, “prepend-build” and so on. While this sounds nitpicky, it’s for readability since it’s maddening to be reading someone’s yaml file and scrolling through their lengthy “override-build”, only to notice “craftctl default” buried somewhere in the middle or, even worse, at the very end. Being able to use “append” and “prepend” would open up the possibility of far more readable overrides.

Is there a replacement to SNAPCRAFT_BUILD_INFO=y in snapcraft 7? CRAFT_BUILD_INFO does not seem to do the trick.

How to have a part with global files with core22? I have a part with patches that stages them and then other parts depend on that part and are using the patches in override-pull, but that no more works, $CRAFT_STAGE doesn’t contain the patches… How is this use-case supposed to work now?

And setting LD_LIBRARY_PATH when building a part leads to /bin/bash: line 41: LD_LIBRARY_PATH: unbound variable . This motivated me to create a bug report about regression since Snapcraft 4.0: https://bugs.launchpad.net/snapcraft/+bug/1978859.

I’m trying to migrate a snap from core20 to core22. I followed the steps above.

I’m seeing the following:

Bad snapcraft.yaml content:
- extra field 'run-on' not permitted in 'architectures[0]' configuration
- extra field 'filesets' not permitted in 'parts.mypart' configuration

So it looks like the validations for core22 are more strict for these fields?

I should be able to resolve these errors myself, but I wanted to contribute to this migration documentation.

It’s not implemented yet. Manifests should be added soon, including annotations.

Architectures support is currently being worked on, and the build-on/run-on entries were renamed to build-on/build-to to better match the on/to entries used in advanced grammar (but remember, there is no try).

Fileset definitions will be changed to be more flexible, but you can manually expand them as a workaround until the new implementation is released.

1 Like

The recommended way to handle local patches is to apply them directly instead of staging them. You can use places like $CRAFT_PROJECT_DIR/<patches-dir> or snap/local to store the patch files.

Patches are for third-party libraries.

Ah, I see, it should be accessible from any part… It’s a pity it doesn’t really fit into our workflow, we have a centralized repository with patches for third-party libraries used in multiple projects…

One possibility could be to apply the patches prior to building in override-build since it will run after the dependency is staged, but some extra care is needed to make patching not fail as already applied when updating the build, while still handling the error in case of failure applying the patch. Unfortunately it seems that patch(1) doesn’t have an option for that, so some scripting would be required. Or you maybe you can pull the patches from the centralized repository along with the part to be patched?

Well, I found another workaround: I copy to $CRAFT_STAGE manually in override-pull, remove the copied folder in override-stage and then stage: [-./*] to avoid snapcraft removing non-existing files

Or maybe a git submodule could make all the remote patches appear as local, avoiding more complicated setups.

Should this not say “build-for”, not “build-to”?

Yes, it’s build-for. Unfortunately it seems that I can’t edit/update the previous post to fix it.