Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Change detection

firmware-action has basic detection of changes.

Related temporary files to aid in change detection are stored in .firmware-action/ directory, which is always created in current working directory.

Note

It is save to delete the .firmware-action/ directory, but keep in mind that change detection depends on its existence.

If .firmware-action/ directory is deleted, it will be re-created on next firmware-action execution.

It might be advantageous to also include this directory in caches / artifacts when in CI. Preserving these files might reduce run time in the CI.

Sources modification time

When building a module, firmware-action checks recursively all sources for a given module. For all module types, list of sources include repository and all input files.

Code snippet: Common sources


// GetSources returns slice of paths to all sources which are used for build
func (opts CommonOpts) GetSources() []string {
	sources := []string{}

	// Repository path
	sources = append(sources, opts.RepoPath)

	// Input files and directories
	sources = append(sources, opts.InputDirs[:]...)
	sources = append(sources, opts.InputFiles[:]...)

	return sources
}

Each module type has then additional source files. For example coreboot, where list of sources also includes defconfig and all the blobs.

Code snippet: Additional coreboot sources


// GetSources returns slice of paths to all sources which are used for build
func (opts CorebootOpts) GetSources() []string {
	sources := opts.CommonOpts.GetSources()

	// Add DefconfigPath to list of sources
	sources = append(sources, opts.DefconfigPath)

	// Add blobs to list of sources
	blobs, err := opts.ProcessBlobs()
	if err != nil {
		slog.Error(
			"Failed to process all blobs",
			slog.Any("error", err),
		)

		return nil
	}

	pwd, err := os.Getwd()
	if err != nil {
		slog.Error(
			"Could not get working directory",
			slog.String("suggestion", logging.ThisShouldNotHappenMessage),
			slog.Any("error", err),
		)

		return nil
	}

	for blob := range blobs {
		// Path to local file on host
		src := filepath.Join(
			pwd,
			blobs[blob].Path,
		)
		sources = append(sources, src)
	}

	return sources
}

When a module is successfully built, a file containing time stamp is saved to .firmware-action/timestamps/ directory.

Note

The saved time stamp is the time of the check (when was the check performed), and not the time when module is successfully built.

On next run, this file (if exists) is loaded with time stamp of last successful run. Then all sources are recursively checked for any file that was modified since the last successful run. If no file was modified since the loaded time stamp, module is considered up-to-date and build is skipped. If any of the files has newer modified time, module is re-built.

False positives

Warning

As mentioned in configuration section, you should avoid nested outputs (placing output of one module into output of another module).

However you should also avoid nesting output inside of repo_path.

When firmware-action is checking for modification time, it checks the entirety of the repo_path (except .git directory), and any single file newer than the saved time stamp will trigger rebuild.

This means that if the output is inside repo_path, it will always rebuild because the output artifacts will always be newer that the saved time stamp, and the output in this case is always searched as part of repo_path.

Ideal setup:
  .
  ├── repo_path/
  └── output-uroot/
      └── uroot.bin

Will always rebuild:
  .
  └── repo_path/
      └── output-uroot/
          └── uroot.bin

Configuration file changes

firmware-action can also detect changes in the configuration file. For each module, on each successful build, it stores a copy of the configuration in .firmware-action/configs/ directory.

On next run, current configuration is compared to configuration of last successful build, and if the configuration for the specific module differs, module is re-built.

Git commit hash changes

firmware-action can detect changes based on git commit hashes. For each module, on each successful build, it stores the git commit hash of the module's repository path (repo_path) in .firmware-action/git-hash/ directory.

On next run, the current git commit hash of the module's repository is compared to the stored hash from the last successful build. If the hashes differ, indicating that the module's repository has been changed, the module is re-built.