> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Source operations

> Run automated code updates via source operations.

export const DynamicCodeBlock = ({language = 'yaml', filename, icon, lines, wrap, expandable, highlight, focus, children}) => {
  const STORAGE_KEY = 'upsun_versions_cache';
  const COMPOSABLE_STORAGE_KEY = 'upsun_composable_cache';
  const CACHE_TTL = 5 * 60 * 1000;
  const API_URL = 'https://meta.upsun.com/images';
  const COMPOSABLE_API_URL = 'https://meta.upsun.com/composable';
  const DEBUG_PREFIX = '[DynamicCodeBlock cache]';
  const [versionData, setVersionData] = useState(null);
  const [versionError, setVersionError] = useState(false);
  const [composableData, setComposableData] = useState(null);
  const [composableError, setComposableError] = useState(false);
  useEffect(() => {
    const fetchData = async () => {
      let cachedData = null;
      let cachedEtag = null;
      if (typeof localStorage !== 'undefined') {
        try {
          const cached = localStorage.getItem(STORAGE_KEY);
          if (cached) {
            const parsed = JSON.parse(cached);
            cachedData = parsed?.data || null;
            cachedEtag = parsed?.etag || null;
            if (cachedData && Date.now() - parsed.timestamp < CACHE_TTL) {
              return cachedData;
            }
          }
        } catch (err) {
          console.error('Failed to load from cache:', err);
        }
      }
      const requestHeaders = cachedEtag ? {
        'If-None-Match': cachedEtag
      } : {};
      console.debug(`${DEBUG_PREFIX} revalidating`, {
        storageKey: STORAGE_KEY,
        hasCachedData: Boolean(cachedData),
        hasCachedEtag: Boolean(cachedEtag)
      });
      const response = await fetch(API_URL, {
        headers: requestHeaders
      });
      if (response.status === 304 && cachedData) {
        console.debug(`${DEBUG_PREFIX} revalidated (304)`, {
          storageKey: STORAGE_KEY
        });
        if (typeof localStorage !== 'undefined') {
          try {
            const etag = response.headers.get('etag') || cachedEtag;
            localStorage.setItem(STORAGE_KEY, JSON.stringify({
              data: cachedData,
              etag,
              timestamp: Date.now()
            }));
          } catch (err) {
            console.error('Failed to refresh cache metadata:', err);
          }
        }
        return cachedData;
      }
      if (!response.ok) throw new Error(`API request failed: ${response.statusText}`);
      const data = await response.json();
      const etag = response.headers.get('etag');
      console.debug(`${DEBUG_PREFIX} refreshed (200)`, {
        storageKey: STORAGE_KEY,
        etag
      });
      if (typeof localStorage !== 'undefined') {
        try {
          localStorage.setItem(STORAGE_KEY, JSON.stringify({
            data,
            etag,
            timestamp: Date.now()
          }));
        } catch (err) {
          console.error('Failed to cache data:', err);
        }
      }
      return data;
    };
    fetchData().then(data => setVersionData(data)).catch(err => console.error('Failed to fetch version data:', err));
  }, []);
  const findHighestVersion = versionsMap => {
    if (!versionsMap || Object.keys(versionsMap).length === 0) return null;
    const entries = Object.entries(versionsMap);
    const active = entries.filter(([, v]) => v.upsun && v.upsun.status === 'supported' || v.upsun && v.upsun.status === 'deprecated');
    const candidates = active.length > 0 ? active : entries;
    let [highestName] = candidates[0];
    for (let i = 1; i < candidates.length; i++) {
      const [currentName] = candidates[i];
      const cp = currentName.split('.').map(Number);
      const hp = highestName.split('.').map(Number);
      for (let j = 0; j < Math.max(cp.length, hp.length); j++) {
        if ((cp[j] || 0) > (hp[j] || 0)) {
          highestName = currentName;
          break;
        } else if ((cp[j] || 0) < (hp[j] || 0)) {
          break;
        }
      }
    }
    return highestName;
  };
  const getVersion = (lang, requestedVersion = 'latest') => {
    if (lang === 'composable') {
      if (!composableData || !composableData.versions || Object.keys(composableData.versions).length === 0) return null;
      if (requestedVersion && requestedVersion !== 'latest') {
        return (requestedVersion in composableData.versions) ? requestedVersion : null;
      }
      return findHighestVersion(composableData.versions);
    }
    if (!versionData) return null;
    const imageData = versionData[lang];
    if (!imageData || !imageData.versions || Object.keys(imageData.versions).length === 0) {
      return null;
    }
    if (requestedVersion && requestedVersion !== 'latest') {
      return (requestedVersion in imageData.versions) ? requestedVersion : null;
    }
    return findHighestVersion(imageData.versions);
  };
  let code = typeof children === 'string' ? children : String(children || '');
  const codeLines = code.split('\n');
  while (codeLines.length > 0 && codeLines[0].trim() === '') codeLines.shift();
  while (codeLines.length > 0 && codeLines[codeLines.length - 1].trim() === '') codeLines.pop();
  if (codeLines.length > 0) {
    const indents = codeLines.filter(line => line.trim().length > 0).map(line => line.match(/^[ \t]*/)[0].length);
    const minIndent = Math.min(...indents);
    code = codeLines.map(line => line.slice(minIndent)).join('\n');
  }
  code = code.replace(/\{\{version:(.*?)\}\}/g, (match, params) => {
    const parts = params.split(':');
    const lang = parts[0];
    const ver = parts[1] || 'latest';
    const isComposable = lang === 'composable';
    const hasError = isComposable ? composableError : versionError;
    const dataReady = isComposable ? composableData !== null : versionData !== null;
    if (hasError) return '(unavailable)';
    if (dataReady) {
      const resolvedVersion = getVersion(lang, ver);
      return resolvedVersion || match;
    }
    return '...';
  });
  const codeBlockProps = {
    language,
    ...filename && ({
      filename
    }),
    ...icon && ({
      icon
    }),
    ...lines !== undefined && ({
      lines
    }),
    ...wrap !== undefined && ({
      wrap
    }),
    ...expandable !== undefined && ({
      expandable
    }),
    ...highlight && ({
      highlight
    }),
    ...focus && ({
      focus
    })
  };
  return <CodeBlock {...codeBlockProps}>{code}</CodeBlock>;
};

export const VariableBlock = ({name}) => {
  return <var spellCheck={false} title={`Replace '${name}' with your own data`}>{name}</var>;
};

export const SettingsIcon = () => <svg width="24px" height="24px" style={{
  display: 'inline',
  verticalAlign: 'middle'
}}>
    <title id="settings-icon">Settings</title>
    <path d="M10.5289 21.9416C9.86066 21.8582 9.3595 21.3576 9.19245 20.6903L9.02539 19.6892C8.85834 19.6058 8.77482 19.6058 8.60776 19.5224L7.93955 20.0229C7.43839 20.44 6.68665 20.44 6.10196 20.0229C6.10196 20.0229 4.09732 18.1042 4.01379 17.9374C3.51263 17.3535 3.51263 16.6027 3.93027 16.0187L4.51495 15.3514C4.43143 15.1845 4.43143 15.1011 4.3479 14.9343L3.42911 14.7674C2.76089 14.684 2.25973 14.1835 2.09268 13.4327C2.00915 13.1824 2.09268 10.4296 2.09268 10.4296C2.17621 9.76222 2.67737 9.2617 3.34558 9.09486L4.3479 8.92802C4.43143 8.8446 4.43143 8.76118 4.51495 8.59434L4.01379 7.92697C3.59616 7.42645 3.59616 6.67567 4.01379 6.09173C4.01379 6.09173 5.93491 4.08964 6.10196 4.00622C6.68665 3.5057 7.43839 3.5057 8.02307 3.9228L8.69129 4.50675C8.85834 4.42333 8.94187 4.42333 9.10892 4.33991L9.27597 3.42228C9.3595 2.75492 9.86066 2.2544 10.6124 2.08756C10.863 2.00414 13.2853 2.00414 13.5358 2.08756H13.6194C14.2876 2.17098 14.7887 2.6715 14.9558 3.33886L15.1228 4.33991C15.2064 4.42333 15.2899 4.50675 15.457 4.50675L16.1252 4.00622C16.6263 3.58912 17.3781 3.58912 17.9628 4.00622C17.9628 4.00622 19.9674 5.92489 20.0509 6.09173C20.5521 6.67567 20.5521 7.42645 20.1344 8.0104L19.6333 8.67776C19.7168 8.8446 19.7168 9.01144 19.8003 9.09486L20.7191 9.2617C21.3873 9.34512 21.972 9.92906 22.0556 10.5964V13.5996C21.972 14.2669 21.4709 14.7674 20.8027 14.9343L19.8003 15.1011C19.7168 15.268 19.7168 15.3514 19.6333 15.5182L20.218 16.269C20.6356 16.7695 20.6356 17.5203 20.218 18.1042L18.0463 20.2732C17.4616 20.6903 16.7099 20.6903 16.2087 20.2732L15.3734 19.6058C15.2064 19.6892 15.1228 19.6892 14.9558 19.7726L14.7887 20.6903C14.7052 21.3576 14.1205 21.9416 13.4523 22.025H10.5289V21.9416ZM11.5312 20.3566C11.8653 20.3566 12.1994 20.3566 12.45 20.3566C12.8676 20.3566 13.2017 20.0229 13.2853 19.6058L13.3688 18.9384C13.3688 18.6882 13.6194 18.4379 13.8699 18.2711L15.1228 17.7706C15.3734 17.6871 15.7075 17.6871 15.9581 17.854L16.5428 18.2711C16.8769 18.5213 17.3781 18.5213 17.6286 18.1877C17.7957 18.0208 18.0463 17.7706 18.2969 17.5203C18.5474 17.1866 18.631 16.7695 18.3804 16.4358L17.8792 16.0187C17.7122 15.7685 17.6286 15.4348 17.7957 15.1845L18.2969 14.0167C18.3804 13.7664 18.631 13.5996 18.9651 13.5161L19.6333 13.4327C20.0509 13.3493 20.385 13.0156 20.385 12.5985C20.385 12.2648 20.385 11.9311 20.385 11.6809C20.385 11.2638 20.0509 10.9301 19.6333 10.8467L18.9651 10.7633C18.7145 10.7633 18.4639 10.513 18.2969 10.2627L17.7957 9.01144C17.7122 8.76118 17.7122 8.4275 17.8792 8.17724L18.2969 7.59329C18.5474 7.25961 18.5474 6.84251 18.2133 6.50883C18.0463 6.34199 17.7957 6.09173 17.5451 5.84147C17.211 5.50779 16.7934 5.50779 16.4593 5.75805L16.0416 6.17515C15.7911 6.34199 15.457 6.42541 15.2064 6.25857L14.037 5.75805C13.7864 5.67463 13.6194 5.42437 13.5358 5.09069L13.3688 4.50675C13.2853 4.08964 12.9511 3.75596 12.5335 3.75596C12.1994 3.75596 11.8653 3.75596 11.6147 3.75596C11.1971 3.75596 10.863 4.08964 10.7795 4.50675L10.6959 5.17411C10.6959 5.42437 10.4453 5.67463 10.1948 5.84147L9.02539 6.25857C8.69129 6.42541 8.44071 6.34199 8.19013 6.17515L7.60544 5.75805C7.27133 5.50779 6.77017 5.50779 6.51959 5.84147C6.26901 6.00831 6.10196 6.25857 5.85138 6.42541C5.6008 6.75909 5.51727 7.17619 5.85138 7.50987L6.18549 8.09382C6.35254 8.34408 6.43607 8.67776 6.26901 8.92802L5.76785 10.0959C5.68433 10.3462 5.43375 10.513 5.09964 10.5964L4.43143 10.6798C4.01379 10.7633 3.67969 11.0969 3.67969 11.514C3.67969 11.8477 3.67969 12.1814 3.67969 12.4317C3.67969 12.8488 4.01379 13.1825 4.43143 13.2659L5.09964 13.3493C5.35022 13.3493 5.6008 13.5996 5.76785 13.8498L6.26901 15.1011C6.35254 15.3514 6.35254 15.6851 6.18549 15.9353L5.76785 16.5193C5.51727 16.8529 5.51727 17.3535 5.85138 17.6037C6.01843 17.7706 6.26901 18.0208 6.51959 18.2711C6.8537 18.5213 7.27133 18.6048 7.60544 18.3545L8.02307 17.854C8.27365 17.6871 8.60776 17.6037 8.85834 17.7706L10.0277 18.2711C10.2783 18.3545 10.4453 18.6048 10.5289 18.9384L10.6124 19.6058C10.7795 20.0229 11.1136 20.3566 11.5312 20.3566ZM18.798 6.92593C18.798 6.92593 18.798 7.00935 18.798 6.92593V6.92593Z"></path>
    <path d="M12.0324 17.0198C9.27597 17.0198 7.02075 14.7675 7.02075 12.0146C7.02075 9.26172 9.27597 7.00937 12.0324 7.00937C14.7887 7.00937 17.044 9.26172 17.044 12.0146C17.044 14.7675 14.7887 17.0198 12.0324 17.0198ZM12.0324 8.67777C10.1948 8.67777 8.69129 10.1793 8.69129 12.0146C8.69129 13.8498 10.1948 15.3514 12.0324 15.3514C13.8699 15.3514 15.3734 13.8498 15.3734 12.0146C15.3734 10.1793 13.8699 8.67777 12.0324 8.67777Z"></path>
  </svg>;

export const MoreIcon = () => <svg width="24px" height="24px" style={{
  display: 'inline',
  verticalAlign: 'middle'
}}>
    <title id="more-icon">More</title>
    <path d="M12 6.5C12.8284 6.5 13.5 5.82843 13.5 5C13.5 4.17157 12.8284 3.5 12 3.5C11.1716 3.5 10.5 4.17157 10.5 5C10.5 5.82843 11.1716 6.5 12 6.5Z" fill="current"></path>
    <path d="M12 13.5C12.8284 13.5 13.5 12.8284 13.5 12C13.5 11.1716 12.8284 10.5 12 10.5C11.1716 10.5 10.5 11.1716 10.5 12C10.5 12.8284 11.1716 13.5 12 13.5Z" fill="current"></path>
    <path d="M12 20.5C12.8284 20.5 13.5 19.8284 13.5 19C13.5 18.1716 12.8284 17.5 12 17.5C11.1716 17.5 10.5 18.1716 10.5 19C10.5 19.8284 11.1716 20.5 12 20.5Z" fill="current"></path>
  </svg>;

On Upsun, you can run automated code updates through a feature called **source operations**.
Defined in your [app configuration](/docs/configure-apps), source operations let you specify commands
that can commit changes to your project's repository when called.

For example, you can set up a source operation to [automatically update your application dependencies](/docs/core-concepts/common-tasks/dependency-updates),
[update a site from an upstream repository](#update-a-site-from-an-upstream-repository-or-template),
or [revert to the last commit](#revert-to-the-last-commit) pushed to your Git repository.

To run your source operations, you can use the [Upsun CLI](/cli) or the [Console](https://console.upsun.com).
If you want to run your source operations and update your code automatically,
you can also define [cron jobs](/docs/configure-apps/image-properties/crons).

## How source operations work

When you trigger a source operation, the following happens in order:

1. The current environment HEAD commit is checked out in Git.
   It doesn't have any remotes or tags defined in the project.
   It only has the current environment branch.

2. Sequentially, for each app that has an operation bearing [the name](#define-a-source-operation)
   of the triggered source operation in its configuration,
   the operation command is run in the app container.
   The container isn't part of the environment's runtime cluster
   and doesn't require that the environment is running.

   The environment has all of the variables normally available during the build phase.
   These may be optionally overridden by the variables specified when the operation is run.

3. If any new commits were created, they're pushed to the repository and the normal build process is triggered.

   If multiple apps in a single project both result in a new commit,
   there are two distinct commits in the Git history but only a single new build process.

## Define a source operation

A source operation requires two things:

* A name that must be unique within the application.
  The name is the key of the block defined under `source.operations` in your [app configuration](/docs/configure-apps/image-properties/source).

* A `command` that defines what's run when the operation is triggered.

The syntax is similar to the following:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          type: nodejs:{{version:nodejs:latest}}
          source:
            root: "/"
            operations:
              <SOURCE_OPERATION_NAME>:
                  command: <COMMAND>`
  }
</DynamicCodeBlock>

For example, to update a file from a remote location, you could define an operation like this:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          type: nodejs:{{version:nodejs:latest}}
          source:
            root: "/"
            operations:
              update-file:
                command: |
                  set -e
                  curl -O https://example.com/myfile.txt
                  git add myfile.txt
                  git commit -m "Update remote file"`
  }
</DynamicCodeBlock>

The name of the source operation in this case is `update-file`.

For more possibilities, see other [source operation examples](#source-operation-examples).

## Run a source operation

<Tabs>
  <Tab title="In the Console">
    1. Navigate to the environment where you want to run the operation.
    2. Click <MoreIcon /> **More**.
    3. Click **Run source operation**.
    4. Select the operation you want to run.
    5. Optional: Add the [variables](#use-variables-in-your-source-operations) required by your source operation.
    6. Click **Run**.
  </Tab>

  <Tab title="Using the CLI">
    Run the following command:

    ```bash theme={null}
    upsun source-operation:run <SOURCE_OPERATION_NAME>
    ```

    Replace `<SOURCE_OPERATION_NAME>` with the name of your operation, such as `update-file` in the [example above](#define-a-source-operation).
  </Tab>
</Tabs>

After running a source operation,
to apply the changes to your local development environment run the `git pull` command.

Note that you can [cancel pending or running source operations](/docs/environments/cancel-activity).

## Use variables in your source operations

You can add [variables](/docs/development/variables) to the environment of the source operation.

Use the `env:` prefix to expose each of those variables as a Unix environment variable.
In this way, they're referenced by the source operation
and interpreted the same way as any other variable set in your project.

For example, you might want to have a `FILE` variable available with the value `example.txt`
to pass to a source operation similar to the following:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          type: nodejs:{{version:nodejs:latest}}
          source:
            root: "/"
            operations:
              update-file:
                command: |
                  set -e
                  curl -O https://example.com/$FILE
                  git add $FILE
                  git commit -m "Update remote file"`
  }
</DynamicCodeBlock>

Follow these steps to run the source operation:

<Tabs>
  <Tab title="From the CLI">
    ```bash Using the CLI theme={null}
    upsun source-operation:run update-file --variable env:FILE="example.txt"
    ```
  </Tab>

  <Tab title="From the Console">
    1. Navigate to the environment where you want to run the operation.
    2. Click <MoreIcon /> **More**.
    3. Click **Run source operation**.
    4. Select the operation you want to run.
    5. Under **Add/override variables**, put `FILE` as the **Variable name** and `example.txt` as the **Value**.
       The variable is automatically prefixed with `env:`.
    6. Click **Run**.
  </Tab>
</Tabs>

## Source integrations

If your project is using a [source integration](/docs/integrations/source),
any new commits resulting from a source operation are first pushed to your external Git repository.
Then the source integration pushes those commits to Upsun and redeploys the environment.

When using a source integration,
you can't run source operations on environments created from pull or merge requests created on the external repository.

If you try running a source operation on a non-supported environment, you see the following error:

```text {no-copy="true"} theme={null}
[ApiFeatureMissingException]
This project doesn't support source operations.
```

## Automated source operations using a cron job

You can use a cron to automatically run your source operations.

Note that it’s best not to run source operations on your production environment,
but rather on a dedicated environment where you can test changes.

Make sure you have the [Upsun CLI](/cli) installed
and [an API token](/cli/api-tokens#2-create-an-api-token)
so you can run a cron job in your app container.

1. Set your API token as a top-level environment variable:

<Tabs>
  <Tab title="From the CLI">
    Run the following command:

    ```bash theme={null}
    upsun variable:create --environment main --level environment --prefix 'env' --name UPSUN_CLI_TOKEN --sensitive true --value 'YOUR_UPSUN_CLI_TOKEN' --inheritable false --visible-build true --json false --enabled true --visible-runtime true
    ```
  </Tab>

  <Tab title="From the Console">
    1. Open the environment where you want to add the variable.
    2. Click <SettingsIcon /> **Settings**.
    3. Click **Variables**.
    4. Click **+ Add variable**.
    5. In the **Variable name** field, enter `env:UPSUN_CLI_TOKEN`.
    6. In the **Value** field, enter your API token.
    7. Make sure the **Available at runtime** and **Sensitive variable** options are selected.
    8. Click **Add variable**.
  </Tab>
</Tabs>

<Warning>
  Once you add the API token as an environment variable,
  anyone with [SSH access](/docs/development/ssh) can read its value.
  Make sure you carefully check your [user access on this project](/docs/administration/users#manage-project-users).
</Warning>

2. Add a build hook to your app configuration to install the CLI as part of the build process:

```yaml .upsun/config.yaml theme={null}
applications:
  myapp:
    hooks:
      build: |
        set -e
        echo "Installing Upsun CLI"
        curl -fsSL https://raw.githubusercontent.com/upsun/cli/main/installer.sh | bash

        echo "Testing Upsun CLI"
        upsun
```

3. Then, to configure a cron job to automatically run a source operation once a day,
   use a configuration similar to the following:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          type: nodejs:{{version:nodejs:latest}}
          source:
            root: "/"
            operations:
              update-file:
                command: |
                  set -e
                  curl -O https://example.com/$FILE
                  git add $FILE
                  git commit -m "Update remote file"
          crons:
            update:
              # Run the code below every day at midnight.
              spec: '0 0 * * *'
              commands:
                start: |
                  set -e
                  upsun sync -e development code data --no-wait --yes
                  upsun source-operation:run update-file --no-wait --yes`
  }
</DynamicCodeBlock>

The example above synchronizes the `development` environment with its parent
and then runs the `update-file` source operation defined [previously](#define-a-source-operation).

## Source operation examples

### Update your application dependencies

You can set up a source operation and a cron job to [automate your dependency updates](/docs/core-concepts/common-tasks/dependency-updates).

### Update a site from an upstream repository or template

The following source operation syncronizes your branch with an upstream Git repository.

1. [Add a project-level variable](/docs/development/variables/set-variables#create-project-variables)
   named `env:UPSTREAM_REMOTE` with the Git URL of the upstream repository.
   That makes that repository available as a Unix environment variable in all environments,
   including in the source operation's environment.

   * Variable name: `env:UPSTREAM_REMOTE`
   * Variable example value: `https://github.com/platformsh/platformsh-docs`

2. In your app configuration, define a source operation to fetch from that upstream repository:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          type: nodejs:{{version:nodejs:latest}}
          source:
            root: "/"
            operations:
              upstream-update:
                command: |
                  set -e
                  git remote add upstream $UPSTREAM_REMOTE
                  git fetch --all
                  git merge upstream/main`
  }
</DynamicCodeBlock>

3. Now every time you run the `upstream-update` operation on a given branch,
   the branch fetches all changes from the upstream git repository
   and then merges the latest changes from the default branch in the upstream repository.
   If there’s a conflict merging from the upstream repository,
   the source operation fails and doesn't update from the upstream repository.

Run the `upstream-update` operation on a preview environment rather than directly on Production.

### Revert to the last commit

The following source operation reverts the last commit pushed to the Git repository.
This can be useful if you didn't properly test the changes of another operation
and you need to quickly revert to the previous state.

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          type: nodejs:{{version:nodejs:latest}}
          source:
            root: "/"
            operations:
              revert:
                command: |
                  git reset --hard HEAD~`
  }
</DynamicCodeBlock>

Now every time you run the `revert` operation on a given branch,
the operation reverts to the last commit pushed to that branch.

### Update Drupal Core

The following source operation uses Composer to update Drupal Core:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          type: php:{{version:php:latest}}
          source:
            root: "/"
            operations:
              update-drupal-core:
                command: |
                  set -e
                  composer update drupal/core --with-dependencies
                  git add composer.lock
                  git commit -m "Automated Drupal Core update."`
  }
</DynamicCodeBlock>

`--with-dependencies` is used to also update Drupal Core dependencies.
Read more on how to [update Drupal Core via Composer on Drupal.org](https://www.drupal.org/docs/updating-drupal/updating-drupal-core-via-composer).

Now every time you run the `update-drupal-core` operation, it updates Drupal Core.

### Download a Drupal extension

The following source operation downloads a Drupal extension.
You can define the Drupal extension by setting an `EXTENSION` variable
or [overriding it](#use-variables-in-your-source-operations) when running the source operation.

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          type: php:{{version:php:latest}}
          source:
            root: "/"
            operations:
              download-drupal-extension:
                command: |
                  set -e
                  composer require $EXTENSION
                  git add composer.json
                  git commit -am "Automated install of: $EXTENSION via Composer."`
  }
</DynamicCodeBlock>

Now every time you run the `download-drupal-extension` operation, it downloads the defined extension.

If it's a new extension, after the source operation finishes,
you need to enable the new extension via the Drupal management interface or using Drush.

### Update Git submodules

The following source operation updates all Git submodules recursively:

<DynamicCodeBlock language="yaml" filename=".upsun/config.yaml">
  {`
      applications:
        myapp:
          type: php:{{version:php:latest}}
          source:
            root: "/"
            operations:
              rebuild:
                command: |
                  set -e
                  git submodule update --init --recursive
                  git submodule update --remote --checkout
                  SHA=$(git submodule | awk -F' ' '{print $1}' | sed -s 's/+//g')
                  echo -n "$SHA" > .sha
                  git add uppler .sha
                  git commit -m "Updating submodule to commit '$SHA'"`
  }
</DynamicCodeBlock>

Now every time you run the `rebuild` operation, it updates the Git submodules.
