Read a software project from a forge and convert it into the Friendly Forge Format. Read from the Friendly Forge Format and write to a software project in a forge.
Go to file
Earl Warren e85bc74328
All checks were successful
/ backend-checks (push) Successful in 3m8s
test(tree/generic): node.NewFormat & node.{Get,Set}Children
2024-05-19 12:21:38 +02:00
.forgejo/workflows chore: move to code.forgejo.org 2024-05-09 13:30:59 +02:00
api chore: move to code.forgejo.org 2024-05-09 13:30:59 +02:00
cmd chore: move to code.forgejo.org 2024-05-09 13:30:59 +02:00
f3 chore(dependencies): run misspell 2024-05-12 22:48:01 +02:00
forges f3: repositories are references in pull requests 2024-05-11 12:22:45 +02:00
logger logger: trim Critical 2024-01-26 12:18:13 +01:00
main chore: move to code.forgejo.org 2024-05-09 13:30:59 +02:00
options chore: move to code.forgejo.org 2024-05-09 13:30:59 +02:00
tests Forgejo 1.22 is now Forgejo 7 2024-03-05 12:24:42 +08:00
tree test(tree/generic): node.NewFormat & node.{Get,Set}Children 2024-05-19 12:21:38 +02:00
util chore(deadcode): remove util/uri 2024-05-14 07:24:08 +02:00
.deadcode-out test(tree/generic): do not export test functions 2024-05-14 11:16:21 +02:00
.editorconfig add editor config 2023-10-24 15:58:31 +02:00
.gitignore chore(lint): run deadcode 2024-05-14 07:23:12 +02:00
.golangci.yml chore(lint): update .golangci.yml to clear warnings 2024-05-12 23:35:51 +02:00
go.mod chore: move to code.forgejo.org 2024-05-09 13:30:59 +02:00
go.sum forge/forgejo: add web client 2024-01-04 09:52:27 +01:00
LICENSE delete everything and start over 2023-10-13 11:04:26 +02:00
Makefile chore(lint): run deadcode 2024-05-14 07:23:12 +02:00
README.md chore: move to code.forgejo.org 2024-05-09 13:30:59 +02:00

gof3

As a CLI or as a library, GoF3 provides a single operation: mirroring. The origin and destination are designated by the URL of a forge and a path to the resource. For instance, mirror --from-type forgejo --from https://code.forgejo.org/forgejo/lxc-helpers --to-type F3 --to /some/directory will mirror a project in a local directory using the F3 format.

Building

  • Install go >= v1.21
  • make f3-cli
  • ./f3-cli mirror -h

Example

To F3

Login to https://code.forgejo.org and obtain an application token with read permissions at https://code.forgejo.org/user/settings/applications.

f3-cli mirror \
  --from-type forgejo --from-forgejo-url https://code.forgejo.org \
  --from-forgejo-token $codetoken \
  --from-path /forge/organizations/actions/projects/cascading-pr \
  --to-type filesystem --to-filesystem-directory /tmp/cascading-pr

From F3

Run a local Forgejo instance with serials=1 tests/setup-forgejo.sh and obtain an application token with:

docker exec --user 1000 forgejo1 forgejo admin user generate-access-token -u root --raw --scopes 'all,sudo'

Mirror issues

f3-cli mirror \
  --from-type filesystem --from-filesystem-directory /tmp/cascading-pr \
  --from-path /forge/organizations/actions/projects/cascading-pr/issues \
  --to-type forgejo --to-forgejo-url http://0.0.0.0:8781 \
  --to-forgejo-token $localtoken

Visit them at http://0.0.0.0:8781/actions/cascading-pr/issues

Testing

Requirements

The tests require a live GitLab instance as well as a live Forgejo instance and will use up to 16GB of RAM.

  • Install docker
  • make coverage

License

This project is MIT licensed.

Architecture

F3 is a hierarchy designed to be stored in a file system. It is represented in memory with the tree/generic abstract data structure that can be saved and loaded from disk by the forges/filesystem driver. Each forge (e.g. forges/forgejo) is supported by a driver that is responsible for the interactions of each resource (e.g issues, asset, etc.).

Tree

tree/f3 implements a F3 hierarchy based on the tree/generic data structure. The tree has a logger for messages, options defining which forge it relates to and how and a pointer to the root node of the hierarchy (i.e. the forge F3 resource).

The node (tree/generic/node.go) has:

  • a unique id (e.g. the numerical id of an issue)
  • a parent
  • chidren (e.g. issues children are issues, issue children are comments and reactions)
  • a kind that maps to a F3 resource (e.g. issue, etc.)
  • a driver for its concrete implementation for a given forge

It relies on a forge driver for the concrete implemenation of a F3 resource (issue, reaction, repository, etc.). For instance the issues driver for Forgejo is responsible for listing the existing issues and the issue driver is responsible for creating, updating or deleting a Forgejo issue.

F3 archive

The F3 JSON schemas are copied in f3/schemas. Their internal representation and validation is found in a source file named after the resource (e.g. an issue represented by f3/schemas/issue.json is implemented by f3/issue.go).

When a F3 resource includes data external to the JSON file (i.e. a Git repository or an asset file), the internal representation has a function to copy the data to the destination given in argument. For instance:

  • f3/repository.go FetchFunc(destination) will git fetch --mirror the repository to the destination directory.
  • f3/releaseasset.go DownloadFunc() returns a io.ReadCloser that will be used by the caller to copy the asset to its destination.

Options

The Forge options at options/interface.go define the parameters given when a forge is created:

Each forge driver is responsible for registering the options (e.g. Forgejo options) and for registering a factory that will create these options (e.g. Forgejo options registration). In addition to the options that are shared by all forges such as the logger, it may define additional options.

Driver interface

For each F3 resource, the driver is responsible for:

  • copying the f3 argument to FromFormat to the forge
  • ToFormat reads from the forge and convert the data into an f3/resources.go

A driver must have a unique name (e.g. forgejo) and register:

Tree driver

The tree driver functions (e.g. forges/forgejo/tree.go) specialize NullTreeDriver.

  • Factory(ctx context.Context, kind generic.Kind) generic.NodeDriverInterface creates a new node driver for a given Kind.
  • GetPageSize() int returns the default page size.

Node driver

The node driver functions for each Kind (e.g. issues, issue, etc.) specialize NullNodeDriver. The examples are given for the Forgejo issue and issues drivers, matching the REST API endpoint to the driver function.

Options

The options created by the factory are expected to provide the options interfaces:

  • Required
    • LoggerInterface
    • URLInterface
  • Optional
    • CLIInterface if additional CLI arguments specific to the forge are supported

For instance forges/forgejo/options/options.go is created by forges/forgejo/options.go.

Driver implementation

A driver for a forge must be self contained in a directory (e.g. forges/forgejo). Functions shared by multiple forges are grouped in the forges/helpers directory and split into one directory per Kind (e.g. forges/helpers/pullrequest).

  • options.go defines the name of the forge in the Name variable (e.g. Name = "forgejo")
  • options/options.go defines the options specific to the forge and the corresponding CLI flags
  • main.go calls f3_tree.RegisterForgeFactory to create the forge given its name
  • tree.go has the Factory() function that maps a node kind (issue, reaction, etc.) into an object that is capable of interacting with it (CRUD).
  • one file per Kind (e.g. forges/forgejo/issues.go).

Idempotency

Mirroring is idempotent: it will produce the same result if repeated multiple times. The drivers functions are not required to be idempotent.

  • The Put function will only be called if the resource does not already exist.
  • The Patch and Delete functions will only be called if the resource exists.

Identifiers mapping

When a forge (e.g. Forgejo) is mirrored on the filesystem, the identifiers are preserved verbatim (e.g. the issue identifier). When the filesystem is mirrored to a forge, the identifiers cannot always be preserved. For instance if an issue with the identifier 1234 is downloaded from Forgejo and created on another Forgejo instance, it will be allocated an identifier by the Forgejo instance. It cannot request to be given a specific identifier.

References

A F3 resource may reference another F3 resource by a path. For instance the user that authored an issue is represented by /forge/users/1234 where 1234 is the unique identifier of the user. The reference is relative to the forge. The mirroring of a forge to another is responsible for converting the references using the identifier mapping stored in the origin forge. For instance if /forge/users/1234 stored in the filesystem is created in Forgejo as /forge/users/58, the issue stored in the filesystem with its authored as /forge/users/1234 will be created in Forgejo to be authored by /forge/users/58 instead.

Logger

The tree/generic has a pointer to a logger implementing logger.Interface which is made available to the nodes and the drivers.

Context

All functions except for setters and getters have a context.Context argument which is checked (using util/terminate.go) to not be Done before performing a long lasting operation (e.g. a REST API call or a call to the Git CLI). It is not used otherwise.

Error model

When an error that cannot be recovered from happens, panic is called, otherwise an Error is logged.

CLI

The CLI is in cmd and relies on options to figure out which options are to be implemented for each supported forge.

Hacking

The JSON schemas come from the f3-schemas repository and should be updated as follows:

cd format ; rm -fr schemas ; git --work-tree schemas clone https://lab.forgefriends.org/friendlyforgeformat/f3-schemas ; rm -fr f3-schemas

They cannot be a submodule.

Funding

See the page dedicated to funding in the F3 documentation