Skip to content

Hooks System

Hooks are async functions in your ota-updates.config.js that run at key points during the publish flow. Use them to validate, transform, or extend the default behavior — for example, running tests before production publishes, customizing version formats, or sending Slack notifications after a release.

Available Hooks

javascript
export default {
  hooks: {
    beforePublish: async (context) => {},
    afterPublish: async (version, context) => {},
    onError: async (error, context) => {},
    generateVersion: async (context) => {},
    generateChangelog: async (context) => {},
  },
};

Hook Signatures

beforePublish(context)

Runs before version increment and publish.

context:

ts
{
  currentVersion: OTAVersionData | null;
  channel: string;
  changelog: string[];
  dryRun: boolean;
  cwd: string;
}

Can return optional overrides:

ts
{
  changelog?: string[];
  message?: string;
  version?: string | Partial<OTAVersionData>;
}

afterPublish(version, context)

Runs after successful publish.

version is the final resolved version object.
context:

ts
{
  channel: string;
  message: string;
  cwd: string;
  dryRun: boolean;
}

onError(error, context)

Runs on publish failure.

context:

ts
{
  channel?: string;
  cwd: string;
}

generateVersion(context)

Required when versionStrategy: 'custom'.

context:

ts
{
  currentVersion: OTAVersionData | null;
  channel: string;
  changelog: string[];
  cwd: string;
  buildNumber: number;
  baseVersion: string;
  parsedBaseVersion: { major: number; minor: number; patch: number };
  templateVars: {
    major: number;
    minor: number;
    patch: number;
    channel: string;
    channelAlias: string;
    build: number;
    timestamp: string;
  };
  defaultVersion: string;
}

Return value:

  • string (full version)
  • Partial<OTAVersionData>
  • full OTAVersionData

generateChangelog(context)

Required when changelog.source: 'custom'.

context:

ts
{
  currentVersion: OTAVersionData | null;
  channel: string;
  cwd: string;
}

Must return string[].

Practical Examples

1. Short Production Versions + Custom Message

javascript
export default {
  hooks: {
    beforePublish: async ({ channel, changelog }) => {
      if (channel === 'production') {
        return {
          message: `prod release: ${changelog[0] || 'Update'}`,
        };
      }
    },
  },
};

2. Custom Version Strategy

javascript
export default {
  versionStrategy: 'custom',
  hooks: {
    generateVersion: async ({ templateVars }) => {
      return `r${templateVars.major}.${templateVars.minor}.${templateVars.build}`;
    },
  },
};

3. Custom Changelog Source

javascript
export default {
  changelog: {
    source: 'custom',
  },
  hooks: {
    generateChangelog: async ({ channel }) => {
      return [`Manual curated release for ${channel}`];
    },
  },
};

4. Validation Gate

javascript
export default {
  hooks: {
    beforePublish: async ({ channel, changelog }) => {
      if (channel === 'production' && changelog.length === 0) {
        throw new Error('Production publish requires changelog entries');
      }
    },
  },
};

5. Post-Publish Notification

javascript
export default {
  hooks: {
    afterPublish: async (version, context) => {
      await fetch(process.env.SLACK_WEBHOOK_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          text: `OTA update published: v${version.version} → ${context.channel}`,
        }),
      });
    },
  },
};

6. Run Tests Before Production

javascript
export default {
  hooks: {
    beforePublish: async ({ channel }) => {
      if (channel === 'production') {
        const { execa } = await import('execa');
        await execa('npm', ['test'], { stdio: 'inherit' });
      }
    },
  },
};

Best Practices

  1. Keep hooks deterministic and fast.
  2. Throw errors only for hard-stop validation failures.
  3. Use beforePublish return overrides sparingly and clearly.
  4. Keep custom generators pure (no side effects when possible).
  5. Use environment variables for secrets.

Next Steps

Want to feel the package end-to-end first? Try the Expo Showcase Demo.

Released under the MIT License.